Auto merge of #135318 - compiler-errors:vtable-fixes, r=lcnr

Fix deduplication mismatches in vtables leading to upcasting unsoundness

We currently have two cases where subtleties in supertraits can trigger disagreements in the vtable layout, e.g. leading to a different vtable layout being accessed at a callsite compared to what was prepared during unsizing. Namely:

### #135315

In this example, we were not normalizing supertraits when preparing vtables. In the example,

```
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 () {}

fn main() {
    (&() as &dyn Trait as &dyn Middle<()>).say_hello(&0);
}
```

When we prepare `dyn Trait`, we see a supertrait of `Middle<<() as Identity>::Selff>`, which itself has two supertraits `Supertrait<()>` and `Supertrait<<() as Identity>::Selff>`. These two supertraits are identical, but they are not duplicated because we were using structural equality and *not* considering normalization. This leads to a vtable layout with two trait pointers.

When we upcast to `dyn Middle<()>`, those two supertraits are now the same, leading to a vtable layout with only one trait pointer. This leads to an offset error, and we call the wrong method.

### #135316

This one is a bit more interesting, and is the bulk of the changes in this PR. It's a bit similar, except it uses binder equality instead of normalization to make the compiler get confused about two vtable layouts. In the example,

```
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 () {}

fn main() {
    (&() as &'static dyn for<'a> Trait<&'static (), &'a ()>
        as &'static dyn Trait<&'static (), &'static ()>)
        .say_hello(&0);
}
```

When we prepare the vtable for `dyn for<'a> Trait<&'static (), &'a ()>`, we currently consider the PolyTraitRef of the vtable as the key for a supertrait. This leads two two supertraits -- `Supertrait<&'static ()>` and `for<'a> Supertrait<&'a ()>`.

However, we can upcast[^up] without offsetting the vtable from `dyn for<'a> Trait<&'static (), &'a ()>` to `dyn Trait<&'static (), &'static ()>`. This is just instantiating the principal trait ref for a specific `'a = 'static`. However, when considering those supertraits, we now have only one distinct supertrait -- `Supertrait<&'static ()>` (which is deduplicated since there are two supertraits with the same substitutions). This leads to similar offsetting issues, leading to the wrong method being called.

[^up]: I say upcast but this is a cast that is allowed on stable, since it's not changing the vtable at all, just instantiating the binder of the principal trait ref for some lifetime.

The solution here is to recognize that a vtable isn't really meaningfully higher ranked, and to just treat a vtable as corresponding to a `TraitRef` so we can do this deduplication more faithfully. That is to say, the vtable for `dyn for<'a> Tr<'a>` and `dyn Tr<'x>` are always identical, since they both would correspond to a set of free regions on an impl... Do note that `Tr<for<'a> fn(&'a ())>` and `Tr<fn(&'static ())>` are still distinct.

----

There's a bit more that can be cleaned up. In codegen, we can stop using `PolyExistentialTraitRef` basically everywhere. We can also fix SMIR to stop storing `PolyExistentialTraitRef` in its vtable allocations.

As for testing, it's difficult to actually turn this into something that can be tested with `rustc_dump_vtable`, since having multiple supertraits that are identical is a recipe for ambiguity errors. Maybe someone else is more creative with getting that attr to work, since the tests I added being run-pass tests is a bit unsatisfying. Miri also doesn't help here, since it doesn't really generate vtables that are offset by an index in the same way as codegen.

r? `@lcnr` for the vibe check? Or reassign, idk. Maybe let's talk about whether this makes sense.

<sup>(I guess an alternative would also be to not do any deduplication of vtable supertraits (or only a really conservative subset) rather than trying to normalize and deduplicate more faithfully here. Not sure if that works and is sufficient tho.)</sup>

cc `@steffahn` -- ty for the minimizations
cc `@WaffleLapkin` -- since you're overseeing the feature stabilization :3

Fixes #135315
Fixes #135316
This commit is contained in:
bors 2025-01-31 04:09:11 +00:00
commit c37fbd873a
63 changed files with 998 additions and 821 deletions

View file

@ -6,7 +6,7 @@ use cranelift_module::*;
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::interpret::{AllocId, GlobalAlloc, Scalar, read_target_uint};
use rustc_middle::ty::{Binder, ExistentialTraitRef, ScalarInt};
use rustc_middle::ty::{ExistentialTraitRef, ScalarInt};
use crate::prelude::*;
@ -167,7 +167,9 @@ pub(crate) fn codegen_const_value<'tcx>(
&mut fx.constants_cx,
fx.module,
ty,
dyn_ty.principal(),
dyn_ty.principal().map(|principal| {
fx.tcx.instantiate_bound_regions_with_erased(principal)
}),
);
let local_data_id =
fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
@ -243,7 +245,7 @@ pub(crate) fn data_id_for_vtable<'tcx>(
cx: &mut ConstantCx,
module: &mut dyn Module,
ty: Ty<'tcx>,
trait_ref: Option<Binder<'tcx, ExistentialTraitRef<'tcx>>>,
trait_ref: Option<ExistentialTraitRef<'tcx>>,
) -> DataId {
let alloc_id = tcx.vtable_allocation((ty, trait_ref));
data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not)
@ -460,9 +462,15 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
GlobalAlloc::Memory(target_alloc) => {
data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability)
}
GlobalAlloc::VTable(ty, dyn_ty) => {
data_id_for_vtable(tcx, cx, module, ty, dyn_ty.principal())
}
GlobalAlloc::VTable(ty, dyn_ty) => data_id_for_vtable(
tcx,
cx,
module,
ty,
dyn_ty
.principal()
.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
),
GlobalAlloc::Static(def_id) => {
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
{

View file

@ -61,7 +61,12 @@ pub(crate) fn unsized_info<'tcx>(
old_info
}
}
(_, ty::Dynamic(data, ..)) => crate::vtable::get_vtable(fx, source, data.principal()),
(_, ty::Dynamic(data, ..)) => crate::vtable::get_vtable(
fx,
source,
data.principal()
.map(|principal| fx.tcx.instantiate_bound_regions_with_erased(principal)),
),
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
}
}

View file

@ -90,7 +90,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>(
pub(crate) fn get_vtable<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
ty: Ty<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
) -> Value {
let data_id = data_id_for_vtable(fx.tcx, &mut fx.constants_cx, fx.module, ty, trait_ref);
let local_data_id = fx.module.declare_data_in_func(data_id, fx.bcx.func);

View file

@ -234,7 +234,12 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
GlobalAlloc::VTable(ty, dyn_ty) => {
let alloc = self
.tcx
.global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal())))
.global_alloc(self.tcx.vtable_allocation((
ty,
dyn_ty.principal().map(|principal| {
self.tcx.instantiate_bound_regions_with_erased(principal)
}),
)))
.unwrap_memory();
let init = const_alloc_to_gcc(self, alloc);
self.static_addr_of(init, alloc.inner().align, None)

View file

@ -14,7 +14,7 @@ use rustc_middle::ty::layout::{
FnAbiError, FnAbiOf, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError,
LayoutOfHelpers,
};
use rustc_middle::ty::{self, Instance, PolyExistentialTraitRef, Ty, TyCtxt};
use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt};
use rustc_session::Session;
use rustc_span::source_map::respan;
use rustc_span::{DUMMY_SP, Span};
@ -90,7 +90,7 @@ pub struct CodegenCx<'gcc, 'tcx> {
pub function_instances: RefCell<FxHashMap<Instance<'tcx>, Function<'gcc>>>,
/// Cache generated vtables
pub vtables:
RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>>,
RefCell<FxHashMap<(Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>), RValue<'gcc>>>,
// TODO(antoyo): improve the SSA API to not require those.
/// Mapping from function pointer type to indexes of on stack parameters.
@ -401,7 +401,7 @@ impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> {
impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
fn vtables(
&self,
) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>> {
) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ExistentialTraitRef<'tcx>>), RValue<'gcc>>> {
&self.vtables
}

View file

@ -7,7 +7,7 @@ use rustc_data_structures::sync::Lrc;
use rustc_index::bit_set::DenseBitSet;
use rustc_index::{Idx, IndexVec};
use rustc_middle::mir::{self, Body, SourceScope};
use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
use rustc_middle::ty::{ExistentialTraitRef, Instance, Ty};
use rustc_session::config::DebugInfo;
use rustc_span::{BytePos, Pos, SourceFile, SourceFileAndLine, Span, Symbol};
use rustc_target::abi::Size;
@ -214,7 +214,7 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
fn create_vtable_debuginfo(
&self,
_ty: Ty<'tcx>,
_trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
_trait_ref: Option<ExistentialTraitRef<'tcx>>,
_vtable: Self::Value,
) {
// TODO(antoyo)

View file

@ -314,7 +314,12 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
GlobalAlloc::VTable(ty, dyn_ty) => {
let alloc = self
.tcx
.global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal())))
.global_alloc(self.tcx.vtable_allocation((
ty,
dyn_ty.principal().map(|principal| {
self.tcx.instantiate_bound_regions_with_erased(principal)
}),
)))
.unwrap_memory();
let init = const_alloc_to_llvm(self, alloc, /*static*/ false);
let value = self.static_addr_of_impl(init, alloc.inner().align, None);

View file

@ -77,8 +77,7 @@ pub(crate) struct CodegenCx<'ll, 'tcx> {
/// Cache instances of monomorphic and polymorphic items
pub instances: RefCell<FxHashMap<Instance<'tcx>, &'ll Value>>,
/// Cache generated vtables
pub vtables:
RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), &'ll Value>>,
pub vtables: RefCell<FxHashMap<(Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>), &'ll Value>>,
/// Cache of constant strings,
pub const_str_cache: RefCell<FxHashMap<String, &'ll Value>>,
@ -663,15 +662,14 @@ impl<'ll> SimpleCx<'ll> {
impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn vtables(
&self,
) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), &'ll Value>>
{
) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>), &'ll Value>> {
&self.vtables
}
fn apply_vcall_visibility_metadata(
&self,
ty: Ty<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
poly_trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
vtable: &'ll Value,
) {
apply_vcall_visibility_metadata(self, ty, poly_trait_ref, vtable);

View file

@ -13,7 +13,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_middle::bug;
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf, TyAndLayout};
use rustc_middle::ty::{
self, AdtKind, CoroutineArgsExt, Instance, PolyExistentialTraitRef, Ty, TyCtxt, Visibility,
self, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility,
};
use rustc_session::config::{self, DebugInfo, Lto};
use rustc_span::{DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, Symbol, hygiene};
@ -1399,7 +1399,7 @@ pub(crate) fn build_global_var_di_node<'ll>(
fn build_vtable_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
ty: Ty<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
poly_trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
) -> &'ll DIType {
let tcx = cx.tcx;
@ -1510,7 +1510,7 @@ fn find_vtable_behind_cast<'ll>(vtable: &'ll Value) -> &'ll Value {
pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
ty: Ty<'tcx>,
trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
trait_ref: Option<ExistentialTraitRef<'tcx>>,
vtable: &'ll Value,
) {
// FIXME(flip1995): The virtual function elimination optimization only works with full LTO in
@ -1531,7 +1531,7 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
let vtable = find_vtable_behind_cast(vtable);
let trait_ref_self = trait_ref.with_self_ty(cx.tcx, ty);
let trait_ref_self = cx.tcx.erase_regions(trait_ref_self);
let trait_def_id = trait_ref_self.def_id();
let trait_def_id = trait_ref_self.def_id;
let trait_vis = cx.tcx.visibility(trait_def_id);
let cgus = cx.sess().codegen_units().as_usize();
@ -1590,7 +1590,7 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
pub(crate) fn create_vtable_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
ty: Ty<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
poly_trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
vtable: &'ll Value,
) {
if cx.dbg_cx.is_none() {

View file

@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_macros::HashStable;
use rustc_middle::bug;
use rustc_middle::ty::{self, PolyExistentialTraitRef, Ty, TyCtxt};
use rustc_middle::ty::{self, ExistentialTraitRef, Ty, TyCtxt};
use super::{DefinitionLocation, SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata};
use crate::common::{AsCCharPtr, CodegenCx};
@ -44,7 +44,7 @@ pub(super) enum UniqueTypeId<'tcx> {
/// The ID for the additional wrapper struct type describing an enum variant in CPP-like mode.
VariantStructTypeCppLikeWrapper(Ty<'tcx>, VariantIdx, private::HiddenZst),
/// The ID of the artificial type we create for VTables.
VTableTy(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>, private::HiddenZst),
VTableTy(Ty<'tcx>, Option<ExistentialTraitRef<'tcx>>, private::HiddenZst),
}
impl<'tcx> UniqueTypeId<'tcx> {
@ -88,7 +88,7 @@ impl<'tcx> UniqueTypeId<'tcx> {
pub(crate) fn for_vtable_ty(
tcx: TyCtxt<'tcx>,
self_type: Ty<'tcx>,
implemented_trait: Option<PolyExistentialTraitRef<'tcx>>,
implemented_trait: Option<ExistentialTraitRef<'tcx>>,
) -> Self {
assert_eq!(
self_type,

View file

@ -588,7 +588,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn create_vtable_debuginfo(
&self,
ty: Ty<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
vtable: Self::Value,
) {
metadata::create_vtable_di_node(self, ty, trait_ref, vtable)

View file

@ -25,7 +25,6 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_session::Session;
use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType};
use rustc_span::{DUMMY_SP, Symbol, sym};
use rustc_trait_selection::infer::at::ToTrace;
use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt};
use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
use tracing::{debug, info};
@ -129,14 +128,9 @@ pub fn validate_trivial_unsize<'tcx>(
BoundRegionConversionTime::HigherRankedType,
hr_source_principal,
);
let Ok(()) = ocx.eq_trace(
let Ok(()) = ocx.eq(
&ObligationCause::dummy(),
param_env,
ToTrace::to_trace(
&ObligationCause::dummy(),
hr_target_principal,
hr_source_principal,
),
target_principal,
source_principal,
) else {
@ -211,7 +205,12 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
old_info
}
}
(_, ty::Dynamic(data, _, _)) => meth::get_vtable(cx, source, data.principal()),
(_, ty::Dynamic(data, _, _)) => meth::get_vtable(
cx,
source,
data.principal()
.map(|principal| bx.tcx().instantiate_bound_regions_with_erased(principal)),
),
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
}
}

View file

@ -507,7 +507,7 @@ pub enum VTableNameKind {
pub fn compute_debuginfo_vtable_name<'tcx>(
tcx: TyCtxt<'tcx>,
t: Ty<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
kind: VTableNameKind,
) -> String {
let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
@ -530,8 +530,8 @@ pub fn compute_debuginfo_vtable_name<'tcx>(
}
if let Some(trait_ref) = trait_ref {
let trait_ref = tcx
.normalize_erasing_late_bound_regions(ty::TypingEnv::fully_monomorphized(), trait_ref);
let trait_ref =
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref);
push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
visited.clear();
push_generic_params_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited);

View file

@ -1,5 +1,5 @@
use rustc_middle::bug;
use rustc_middle::ty::{self, GenericArgKind, Ty};
use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt};
use rustc_session::config::Lto;
use rustc_symbol_mangling::typeid_for_trait_ref;
use rustc_target::callconv::FnAbi;
@ -72,12 +72,19 @@ impl<'a, 'tcx> VirtualIndex {
/// This takes a valid `self` receiver type and extracts the principal trait
/// ref of the type. Return `None` if there is no principal trait.
fn dyn_trait_in_self(ty: Ty<'_>) -> Option<ty::PolyExistentialTraitRef<'_>> {
fn dyn_trait_in_self<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Option<ty::ExistentialTraitRef<'tcx>> {
for arg in ty.peel_refs().walk() {
if let GenericArgKind::Type(ty) = arg.unpack()
&& let ty::Dynamic(data, _, _) = ty.kind()
{
return data.principal();
// 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));
}
}
@ -96,7 +103,7 @@ fn dyn_trait_in_self(ty: Ty<'_>) -> Option<ty::PolyExistentialTraitRef<'_>> {
pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
cx: &Cx,
ty: Ty<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
) -> Cx::Value {
let tcx = cx.tcx();
@ -131,7 +138,7 @@ pub(crate) fn load_vtable<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
if bx.cx().sess().opts.unstable_opts.virtual_function_elimination
&& bx.cx().sess().lto() == Lto::Fat
{
if let Some(trait_ref) = dyn_trait_in_self(ty) {
if let Some(trait_ref) = dyn_trait_in_self(bx.tcx(), ty) {
let typeid = bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref)).unwrap();
let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid);
return func;

View file

@ -2,7 +2,7 @@ use std::ops::Range;
use rustc_abi::Size;
use rustc_middle::mir;
use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
use rustc_middle::ty::{ExistentialTraitRef, Instance, Ty};
use rustc_span::{SourceFile, Span, Symbol};
use rustc_target::callconv::FnAbi;
@ -13,7 +13,7 @@ pub trait DebugInfoCodegenMethods<'tcx>: BackendTypes {
fn create_vtable_debuginfo(
&self,
ty: Ty<'tcx>,
trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
trait_ref: Option<ExistentialTraitRef<'tcx>>,
vtable: Self::Value,
);

View file

@ -10,11 +10,11 @@ use super::BackendTypes;
pub trait MiscCodegenMethods<'tcx>: BackendTypes {
fn vtables(
&self,
) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), Self::Value>>;
) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>), Self::Value>>;
fn apply_vcall_visibility_metadata(
&self,
_ty: Ty<'tcx>,
_poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
_poly_trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
_vtable: Self::Value,
) {
}

View file

@ -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(

View file

@ -414,36 +414,33 @@ 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);
if let Some(entry_idx) = vptr_entry_idx {
let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) =
vtable_entries.get(entry_idx)
else {
span_bug!(
self.cur_span(),
"invalid vtable entry index in {} -> {} upcast",
src_pointee_ty,
dest_pointee_ty
);
};
let erased_trait_ref = upcast_trait_ref
.map_bound(|r| ty::ExistentialTraitRef::erase_self_ty(*self.tcx, r));
assert!(
data_b
.principal()
.is_some_and(|b| self.eq_in_param_env(erased_trait_ref, b))
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);
if let Some(entry_idx) = vptr_entry_idx {
let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) =
vtable_entries.get(entry_idx)
else {
span_bug!(
self.cur_span(),
"invalid vtable entry index in {} -> {} upcast",
src_pointee_ty,
dest_pointee_ty
);
} else {
// In this case codegen would keep using the old vtable. We don't want to do
// that as it has the wrong trait. The reason codegen can do this is that
// one vtable is a prefix of the other, so we double-check that.
let vtable_entries_b = self.vtable_entries(data_b.principal(), ty);
assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b);
};
}
let erased_trait_ref =
ty::ExistentialTraitRef::erase_self_ty(*self.tcx, upcast_trait_ref);
assert!(data_b.principal().is_some_and(|b| self.eq_in_param_env(
erased_trait_ref,
self.tcx.instantiate_bound_regions_with_erased(b)
)));
} else {
// In this case codegen would keep using the old vtable. We don't want to do
// that as it has the wrong trait. The reason codegen can do this is that
// one vtable is a prefix of the other, so we double-check that.
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)?;

View file

@ -54,7 +54,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> &'tcx [VtblEntry<'tcx>] {
if let Some(trait_) = trait_ {
let trait_ref = trait_.with_self_ty(*self.tcx, dyn_ty);
let trait_ref = self.tcx.erase_regions(trait_ref);
let trait_ref =
self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
self.tcx.vtable_entries(trait_ref)
} else {
TyCtxt::COMMON_VTABLE_ENTRIES

View file

@ -1136,7 +1136,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
),
rustc_attr!(
TEST, rustc_dump_vtable, Normal, template!(Word),
WarnFollowing, EncodeCrossCrate::Yes
WarnFollowing, EncodeCrossCrate::No
),
rustc_attr!(
TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/),

View file

@ -1,7 +1,8 @@
use rustc_hir as hir;
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_hir::intravisit;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_span::sym;
pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) {
@ -87,3 +88,82 @@ pub(crate) fn def_parents(tcx: TyCtxt<'_>) {
}
}
}
pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
for id in tcx.hir().items() {
let def_id = id.owner_id.def_id;
let Some(attr) = tcx.get_attr(def_id, sym::rustc_dump_vtable) else {
continue;
};
let vtable_entries = match tcx.hir().item(id).kind {
hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => {
let trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate_identity();
if trait_ref.has_non_region_param() {
tcx.dcx().span_err(
attr.span,
"`rustc_dump_vtable` must be applied to non-generic impl",
);
continue;
}
if !tcx.is_dyn_compatible(trait_ref.def_id) {
tcx.dcx().span_err(
attr.span,
"`rustc_dump_vtable` must be applied to dyn-compatible trait",
);
continue;
}
let Ok(trait_ref) = tcx
.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref)
else {
tcx.dcx().span_err(
attr.span,
"`rustc_dump_vtable` applied to impl header that cannot be normalized",
);
continue;
};
tcx.vtable_entries(trait_ref)
}
hir::ItemKind::TyAlias(_, _) => {
let ty = tcx.type_of(def_id).instantiate_identity();
if ty.has_non_region_param() {
tcx.dcx().span_err(
attr.span,
"`rustc_dump_vtable` must be applied to non-generic type",
);
continue;
}
let Ok(ty) =
tcx.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty)
else {
tcx.dcx().span_err(
attr.span,
"`rustc_dump_vtable` applied to type alias that cannot be normalized",
);
continue;
};
let ty::Dynamic(data, _, _) = *ty.kind() else {
tcx.dcx().span_err(attr.span, "`rustc_dump_vtable` to type alias of dyn type");
continue;
};
if let Some(principal) = data.principal() {
tcx.vtable_entries(
tcx.instantiate_bound_regions_with_erased(principal).with_self_ty(tcx, ty),
)
} else {
TyCtxt::COMMON_VTABLE_ENTRIES
}
}
_ => {
tcx.dcx().span_err(
attr.span,
"`rustc_dump_vtable` only applies to impl, or type alias of dyn type",
);
continue;
}
};
tcx.dcx().span_err(tcx.def_span(def_id), format!("vtable entries: {vtable_entries:#?}"));
}
}

View file

@ -152,11 +152,14 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
});
if tcx.features().rustc_attrs() {
tcx.sess.time("outlives_dumping", || outlives::dump::inferred_outlives(tcx));
tcx.sess.time("variance_dumping", || variance::dump::variances(tcx));
collect::dump::opaque_hidden_types(tcx);
collect::dump::predicates_and_item_bounds(tcx);
collect::dump::def_parents(tcx);
tcx.sess.time("dumping_rustc_attr_data", || {
outlives::dump::inferred_outlives(tcx);
variance::dump::variances(tcx);
collect::dump::opaque_hidden_types(tcx);
collect::dump::predicates_and_item_bounds(tcx);
collect::dump::def_parents(tcx);
collect::dump::vtables(tcx);
});
}
// Make sure we evaluate all static and (non-associated) const items, even if unused.

View file

@ -402,6 +402,18 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialTraitRef<'tcx> {
}
}
impl<'tcx> ToTrace<'tcx> for ty::ExistentialTraitRef<'tcx> {
fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> {
TypeTrace {
cause: cause.clone(),
values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(
ty::Binder::dummy(a),
ty::Binder::dummy(b),
)),
}
}
}
impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> {
fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> {
TypeTrace {
@ -410,3 +422,15 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> {
}
}
}
impl<'tcx> ToTrace<'tcx> for ty::ExistentialProjection<'tcx> {
fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> {
TypeTrace {
cause: cause.clone(),
values: ValuePairs::ExistentialProjection(ExpectedFound::new(
ty::Binder::dummy(a),
ty::Binder::dummy(b),
)),
}
}
}

View file

@ -26,7 +26,6 @@ use rustc_parse::{
};
use rustc_passes::{abi_test, input_stats, layout_test};
use rustc_resolve::Resolver;
use rustc_session::code_stats::VTableSizeInfo;
use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
use rustc_session::cstore::Untracked;
use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name};
@ -989,90 +988,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) {
// we will fail to emit overlap diagnostics. Thus we invoke it here unconditionally.
let _ = tcx.all_diagnostic_items(());
});
if sess.opts.unstable_opts.print_vtable_sizes {
let traits = tcx.traits(LOCAL_CRATE);
for &tr in traits {
if !tcx.is_dyn_compatible(tr) {
continue;
}
let name = ty::print::with_no_trimmed_paths!(tcx.def_path_str(tr));
let mut first_dsa = true;
// Number of vtable entries, if we didn't have upcasting
let mut entries_ignoring_upcasting = 0;
// Number of vtable entries needed solely for upcasting
let mut entries_for_upcasting = 0;
let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, tr));
// A slightly edited version of the code in
// `rustc_trait_selection::traits::vtable::vtable_entries`, that works without self
// type and just counts number of entries.
//
// Note that this is technically wrong, for traits which have associated types in
// supertraits:
//
// trait A: AsRef<Self::T> + AsRef<()> { type T; }
//
// Without self type we can't normalize `Self::T`, so we can't know if `AsRef<Self::T>`
// and `AsRef<()>` are the same trait, thus we assume that those are different, and
// potentially over-estimate how many vtable entries there are.
//
// Similarly this is wrong for traits that have methods with possibly-impossible bounds.
// For example:
//
// trait B<T> { fn f(&self) where T: Copy; }
//
// Here `dyn B<u8>` will have 4 entries, while `dyn B<String>` will only have 3.
// However, since we don't know `T`, we can't know if `T: Copy` holds or not,
// thus we lean on the bigger side and say it has 4 entries.
traits::vtable::prepare_vtable_segments(tcx, trait_ref, |segment| {
match segment {
traits::vtable::VtblSegment::MetadataDSA => {
// If this is the first dsa, it would be included either way,
// otherwise it's needed for upcasting
if std::mem::take(&mut first_dsa) {
entries_ignoring_upcasting += 3;
} else {
entries_for_upcasting += 3;
}
}
traits::vtable::VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
// Lookup the shape of vtable for the trait.
let own_existential_entries =
tcx.own_existential_vtable_entries(trait_ref.def_id());
// The original code here ignores the method if its predicates are
// impossible. We can't really do that as, for example, all not trivial
// bounds on generic parameters are impossible (since we don't know the
// parameters...), see the comment above.
entries_ignoring_upcasting += own_existential_entries.len();
if emit_vptr {
entries_for_upcasting += 1;
}
}
}
std::ops::ControlFlow::Continue::<std::convert::Infallible>(())
});
sess.code_stats.record_vtable_size(tr, &name, VTableSizeInfo {
trait_name: name.clone(),
entries: entries_ignoring_upcasting + entries_for_upcasting,
entries_ignoring_upcasting,
entries_for_upcasting,
upcasting_cost_percent: entries_for_upcasting as f64
/ entries_ignoring_upcasting as f64
* 100.,
})
}
}
}
/// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used
@ -1153,12 +1068,6 @@ pub(crate) fn start_codegen<'tcx>(
tcx.sess.code_stats.print_type_sizes();
}
if tcx.sess.opts.unstable_opts.print_vtable_sizes {
let crate_name = tcx.crate_name(LOCAL_CRATE);
tcx.sess.code_stats.print_vtable_sizes(crate_name);
}
codegen
}

View file

@ -95,7 +95,7 @@ impl<'tcx> Key for mir::interpret::GlobalId<'tcx> {
}
}
impl<'tcx> Key for (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>) {
impl<'tcx> Key for (Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>) {
type Cache<V> = DefaultCache<Self, V>;
fn default_span(&self, _: TyCtxt<'_>) -> Span {

View file

@ -1437,9 +1437,9 @@ rustc_queries! {
desc { |tcx| "finding all existential vtable entries for trait `{}`", tcx.def_path_str(key) }
}
query vtable_entries(key: ty::PolyTraitRef<'tcx>)
query vtable_entries(key: ty::TraitRef<'tcx>)
-> &'tcx [ty::VtblEntry<'tcx>] {
desc { |tcx| "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id()) }
desc { |tcx| "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id) }
}
query first_method_vtable_slot(key: ty::TraitRef<'tcx>) -> usize {
@ -1451,7 +1451,7 @@ rustc_queries! {
key.1, key.0 }
}
query vtable_allocation(key: (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>)) -> mir::interpret::AllocId {
query vtable_allocation(key: (Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>)) -> mir::interpret::AllocId {
desc { |tcx| "vtable const allocation for <{} as {}>",
key.0,
key.1.map(|trait_ref| format!("{trait_ref}")).unwrap_or("_".to_owned())

View file

@ -858,7 +858,12 @@ where
}
let mk_dyn_vtable = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
let min_count = ty::vtable_min_entries(tcx, principal);
let min_count = ty::vtable_min_entries(
tcx,
principal.map(|principal| {
tcx.instantiate_bound_regions_with_erased(principal)
}),
);
Ty::new_imm_ref(
tcx,
tcx.lifetimes.re_static,

View file

@ -7,7 +7,7 @@ use rustc_type_ir::elaborate;
use crate::mir::interpret::{
AllocId, AllocInit, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range,
};
use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt};
use crate::ty::{self, Instance, TraitRef, Ty, TyCtxt};
#[derive(Clone, Copy, PartialEq, HashStable)]
pub enum VtblEntry<'tcx> {
@ -22,7 +22,7 @@ pub enum VtblEntry<'tcx> {
/// dispatchable associated function
Method(Instance<'tcx>),
/// pointer to a separate supertrait vtable, can be used by trait upcasting coercion
TraitVPtr(PolyTraitRef<'tcx>),
TraitVPtr(TraitRef<'tcx>),
}
impl<'tcx> fmt::Debug for VtblEntry<'tcx> {
@ -59,7 +59,7 @@ pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2;
// function is an accurate approximation. We verify this when actually computing the vtable below.
pub(crate) fn vtable_min_entries<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
) -> usize {
let mut count = TyCtxt::COMMON_VTABLE_ENTRIES.len();
let Some(trait_ref) = trait_ref else {
@ -67,7 +67,7 @@ pub(crate) fn vtable_min_entries<'tcx>(
};
// This includes self in supertraits.
for def_id in elaborate::supertrait_def_ids(tcx, trait_ref.def_id()) {
for def_id in elaborate::supertrait_def_ids(tcx, trait_ref.def_id) {
count += tcx.own_existential_vtable_entries(def_id).len();
}
@ -83,7 +83,7 @@ pub(crate) fn vtable_min_entries<'tcx>(
/// initial contents.)
pub(super) fn vtable_allocation_provider<'tcx>(
tcx: TyCtxt<'tcx>,
key: (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>),
key: (Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>),
) -> AllocId {
let (ty, poly_trait_ref) = key;
@ -118,7 +118,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
for (idx, entry) in vtable_entries.iter().enumerate() {
let idx: u64 = u64::try_from(idx).unwrap();
let scalar = match entry {
let scalar = match *entry {
VtblEntry::MetadataDropInPlace => {
if ty.needs_drop(tcx, ty::TypingEnv::fully_monomorphized()) {
let instance = ty::Instance::resolve_drop_in_place(tcx, ty);
@ -134,13 +134,12 @@ pub(super) fn vtable_allocation_provider<'tcx>(
VtblEntry::Vacant => continue,
VtblEntry::Method(instance) => {
// Prepare the fn ptr we write into the vtable.
let fn_alloc_id = tcx.reserve_and_set_fn_alloc(*instance, CTFE_ALLOC_SALT);
let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT);
let fn_ptr = Pointer::from(fn_alloc_id);
Scalar::from_pointer(fn_ptr, &tcx)
}
VtblEntry::TraitVPtr(trait_ref) => {
let super_trait_ref = trait_ref
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
let super_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref);
let supertrait_alloc_id = tcx.vtable_allocation((ty, Some(super_trait_ref)));
let vptr = Pointer::from(supertrait_alloc_id);
Scalar::from_pointer(vptr, &tcx)

View file

@ -1138,11 +1138,12 @@ fn create_mono_items_for_vtable_methods<'tcx>(
bug!("create_mono_items_for_vtable_methods: {trait_ty:?} not a trait type");
};
if let Some(principal) = trait_ty.principal() {
let poly_trait_ref = principal.with_self_ty(tcx, impl_ty);
assert!(!poly_trait_ref.has_escaping_bound_vars());
let trait_ref =
tcx.instantiate_bound_regions_with_erased(principal.with_self_ty(tcx, impl_ty));
assert!(!trait_ref.has_escaping_bound_vars());
// Walk all methods of the trait, including those of its supertraits
let entries = tcx.vtable_entries(poly_trait_ref);
let entries = tcx.vtable_entries(trait_ref);
debug!(?entries);
let methods = entries
.iter()
@ -1197,7 +1198,12 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt
}
}
GlobalAlloc::VTable(ty, dyn_ty) => {
let alloc_id = tcx.vtable_allocation((ty, dyn_ty.principal()));
let alloc_id = tcx.vtable_allocation((
ty,
dyn_ty
.principal()
.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
));
collect_alloc(tcx, alloc_id, output)
}
}

View file

@ -1,10 +1,9 @@
use std::cmp;
use rustc_abi::{Align, Size};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lock;
use rustc_span::Symbol;
use rustc_span::def_id::DefId;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct VariantInfo {
@ -71,29 +70,9 @@ pub struct TypeSizeInfo {
pub variants: Vec<VariantInfo>,
}
pub struct VTableSizeInfo {
pub trait_name: String,
/// Number of entries in a vtable with the current algorithm
/// (i.e. with upcasting).
pub entries: usize,
/// Number of entries in a vtable, as-if we did not have trait upcasting.
pub entries_ignoring_upcasting: usize,
/// Number of entries in a vtable needed solely for upcasting
/// (i.e. `entries - entries_ignoring_upcasting`).
pub entries_for_upcasting: usize,
/// Cost of having upcasting in % relative to the number of entries without
/// upcasting (i.e. `entries_for_upcasting / entries_ignoring_upcasting * 100%`).
pub upcasting_cost_percent: f64,
}
#[derive(Default)]
pub struct CodeStats {
type_sizes: Lock<FxHashSet<TypeSizeInfo>>,
vtable_sizes: Lock<FxHashMap<DefId, VTableSizeInfo>>,
}
impl CodeStats {
@ -127,14 +106,6 @@ impl CodeStats {
self.type_sizes.borrow_mut().insert(info);
}
pub fn record_vtable_size(&self, trait_did: DefId, trait_name: &str, info: VTableSizeInfo) {
let prev = self.vtable_sizes.lock().insert(trait_did, info);
assert!(
prev.is_none(),
"size of vtable for `{trait_name}` ({trait_did:?}) is already recorded"
);
}
pub fn print_type_sizes(&self) {
let type_sizes = self.type_sizes.borrow();
// We will soon sort, so the initial order does not matter.
@ -238,33 +209,4 @@ impl CodeStats {
}
}
}
pub fn print_vtable_sizes(&self, crate_name: Symbol) {
// We will soon sort, so the initial order does not matter.
#[allow(rustc::potential_query_instability)]
let mut infos =
std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::<Vec<_>>();
// Primary sort: cost % in reverse order (from largest to smallest)
// Secondary sort: trait_name
infos.sort_by(|a, b| {
a.upcasting_cost_percent
.total_cmp(&b.upcasting_cost_percent)
.reverse()
.then_with(|| a.trait_name.cmp(&b.trait_name))
});
for VTableSizeInfo {
trait_name,
entries,
entries_ignoring_upcasting,
entries_for_upcasting,
upcasting_cost_percent,
} in infos
{
println!(
r#"print-vtable-sizes {{ "crate_name": "{crate_name}", "trait_name": "{trait_name}", "entries": "{entries}", "entries_ignoring_upcasting": "{entries_ignoring_upcasting}", "entries_for_upcasting": "{entries_for_upcasting}", "upcasting_cost_percent": "{upcasting_cost_percent}" }}"#
);
}
}
}

View file

@ -2034,8 +2034,6 @@ options! {
Note that this overwrites the effect `-Clink-dead-code` has on collection!"),
print_type_sizes: bool = (false, parse_bool, [UNTRACKED],
"print layout information for each type encountered (default: no)"),
print_vtable_sizes: bool = (false, parse_bool, [UNTRACKED],
"print size comparison between old and new vtable layouts (default: no)"),
proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
"show backtraces for panics during proc-macro execution (default: no)"),
proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread,

View file

@ -747,7 +747,9 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
let tcx = tables.tcx;
let alloc_id = tables.tcx.vtable_allocation((
ty.internal(&mut *tables, tcx),
trait_ref.internal(&mut *tables, tcx),
trait_ref
.internal(&mut *tables, tcx)
.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
));
Some(alloc_id.stable(&mut *tables))
}

View file

@ -151,7 +151,7 @@ fn symbol_name_provider<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty
pub fn typeid_for_trait_ref<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyExistentialTraitRef<'tcx>,
trait_ref: ty::ExistentialTraitRef<'tcx>,
) -> String {
v0::mangle_typeid_for_trait_ref(tcx, trait_ref)
}

View file

@ -72,7 +72,7 @@ pub(super) fn mangle<'tcx>(
pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyExistentialTraitRef<'tcx>,
trait_ref: ty::ExistentialTraitRef<'tcx>,
) -> String {
// FIXME(flip1995): See comment in `mangle_typeid_for_fnabi`.
let mut cx = SymbolMangler {
@ -84,7 +84,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
binders: vec![],
out: String::new(),
};
cx.print_def_path(trait_ref.def_id(), &[]).unwrap();
cx.print_def_path(trait_ref.def_id, &[]).unwrap();
std::mem::take(&mut cx.out)
}

View file

@ -148,8 +148,6 @@ trait_selection_dtcs_has_req_note = the used `impl` has a `'static` requirement
trait_selection_dtcs_introduces_requirement = calling this method introduces the `impl`'s `'static` requirement
trait_selection_dtcs_suggestion = consider relaxing the implicit `'static` requirement
trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}
trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]`
.label = empty on-clause here

View file

@ -12,7 +12,7 @@ use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty};
use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt};
use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, Region, Ty, TyCtxt};
use rustc_span::{BytePos, Ident, Span, Symbol, kw};
use crate::error_reporting::infer::ObligationCauseAsDiagArg;
@ -22,15 +22,6 @@ use crate::fluent_generated as fluent;
pub mod note_and_explain;
#[derive(Diagnostic)]
#[diag(trait_selection_dump_vtable_entries)]
pub struct DumpVTableEntries<'a> {
#[primary_span]
pub span: Span,
pub trait_ref: PolyTraitRef<'a>,
pub entries: String,
}
#[derive(Diagnostic)]
#[diag(trait_selection_unable_to_construct_constant_value)]
pub struct UnableToConstructConstantValue<'a> {

View file

@ -2,26 +2,22 @@ use std::fmt::Debug;
use std::ops::ControlFlow;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_infer::traits::util::PredicateSet;
use rustc_middle::bug;
use rustc_middle::query::Providers;
use rustc_middle::ty::{
self, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, Upcast, VtblEntry,
};
use rustc_span::{DUMMY_SP, Span, sym};
use rustc_span::DUMMY_SP;
use smallvec::{SmallVec, smallvec};
use tracing::debug;
use crate::errors::DumpVTableEntries;
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> {
MetadataDSA,
TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool },
TraitOwnEntries { trait_ref: ty::TraitRef<'tcx>, emit_vptr: bool },
}
/// Prepare the segments for a vtable
@ -29,7 +25,7 @@ pub enum VtblSegment<'tcx> {
// about our `Self` type here.
pub fn prepare_vtable_segments<'tcx, T>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
) -> Option<T> {
prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value()
@ -39,7 +35,7 @@ pub fn prepare_vtable_segments<'tcx, T>(
/// such that we can use `?` in the body.
fn prepare_vtable_segments_inner<'tcx, T>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
) -> ControlFlow<T> {
// The following constraints holds for the final arrangement.
@ -92,7 +88,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
let mut emit_vptr_on_new_entry = false;
let mut visited = PredicateSet::new(tcx);
let predicate = trait_ref.upcast(tcx);
let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> =
let mut stack: SmallVec<[(ty::TraitRef<'tcx>, _, _); 5]> =
smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))];
visited.insert(predicate);
@ -125,10 +121,18 @@ fn prepare_vtable_segments_inner<'tcx, T>(
let &(inner_most_trait_ref, _, _) = stack.last().unwrap();
let mut direct_super_traits_iter = tcx
.explicit_super_predicates_of(inner_most_trait_ref.def_id())
.explicit_super_predicates_of(inner_most_trait_ref.def_id)
.iter_identity_copied()
.filter_map(move |(pred, _)| {
pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause()
pred.instantiate_supertrait(tcx, ty::Binder::dummy(inner_most_trait_ref))
.as_trait_clause()
})
.map(move |pred| {
tcx.normalize_erasing_late_bound_regions(
ty::TypingEnv::fully_monomorphized(),
pred,
)
.trait_ref
});
// Find an unvisited supertrait
@ -136,16 +140,11 @@ fn prepare_vtable_segments_inner<'tcx, T>(
.find(|&super_trait| visited.insert(super_trait.upcast(tcx)))
{
// Push it to the stack for the next iteration of 'diving_in to pick up
Some(unvisited_super_trait) => {
// We're throwing away potential constness of super traits here.
// FIXME: handle ~const super traits
let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref);
stack.push((
next_super_trait,
emit_vptr_on_new_entry,
maybe_iter(Some(direct_super_traits_iter)),
))
}
Some(next_super_trait) => stack.push((
next_super_trait,
emit_vptr_on_new_entry,
maybe_iter(Some(direct_super_traits_iter)),
)),
// There are no more unvisited direct super traits, dive-in finished
None => break 'diving_in,
@ -154,8 +153,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
// emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() {
let has_entries =
has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id());
let has_entries = has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id);
segment_visitor(VtblSegment::TraitOwnEntries {
trait_ref: inner_most_trait_ref,
@ -169,11 +167,6 @@ fn prepare_vtable_segments_inner<'tcx, T>(
if let Some(next_inner_most_trait_ref) =
siblings.find(|&sibling| visited.insert(sibling.upcast(tcx)))
{
// We're throwing away potential constness of super traits here.
// FIXME: handle ~const super traits
let next_inner_most_trait_ref =
next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings));
// just pushed a new trait onto the stack, so we need to go through its super traits
@ -192,15 +185,6 @@ fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> {
i.into_iter().flatten()
}
fn dump_vtable_entries<'tcx>(
tcx: TyCtxt<'tcx>,
sp: Span,
trait_ref: ty::PolyTraitRef<'tcx>,
entries: &[VtblEntry<'tcx>],
) {
tcx.dcx().emit_err(DumpVTableEntries { span: sp, trait_ref, entries: format!("{entries:#?}") });
}
fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some()
}
@ -239,8 +223,15 @@ fn own_existential_vtable_entries_iter(
/// that come from `trait_ref`, including its supertraits.
fn vtable_entries<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'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);
let mut entries = vec![];
@ -251,33 +242,26 @@ fn vtable_entries<'tcx>(
entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
let existential_trait_ref = trait_ref
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref);
// Lookup the shape of vtable for the trait.
let own_existential_entries =
tcx.own_existential_vtable_entries(existential_trait_ref.def_id());
tcx.own_existential_vtable_entries(existential_trait_ref.def_id);
let own_entries = own_existential_entries.iter().copied().map(|def_id| {
debug!("vtable_entries: trait_method={:?}", def_id);
// The method may have some early-bound lifetimes; add regions for those.
let args = trait_ref.map_bound(|trait_ref| {
// FIXME: Is this normalize needed?
let args = tcx.normalize_erasing_regions(
ty::TypingEnv::fully_monomorphized(),
GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
GenericParamDefKind::Type { .. }
| GenericParamDefKind::Const { .. } => {
trait_ref.args[param.index as usize]
}
})
});
// The trait type may have higher-ranked lifetimes in it;
// erase them if they appear, so that we get the type
// at some particular call site.
let args = tcx.normalize_erasing_late_bound_regions(
ty::TypingEnv::fully_monomorphized(),
args,
}),
);
// It's possible that the method relies on where-clauses that
@ -317,11 +301,6 @@ fn vtable_entries<'tcx>(
let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback);
if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) {
let sp = tcx.def_span(trait_ref.def_id());
dump_vtable_entries(tcx, sp, trait_ref, &entries);
}
tcx.arena.alloc_from_iter(entries)
}
@ -329,14 +308,20 @@ 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!();
};
let source_principal =
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);
let source_principal = tcx.instantiate_bound_regions_with_erased(
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self),
);
let target_principal = ty::Binder::dummy(ty::ExistentialTraitRef::erase_self_ty(tcx, key));
let target_principal = ty::ExistentialTraitRef::erase_self_ty(tcx, key);
let vtable_segment_callback = {
let mut vptr_offset = 0;
@ -346,17 +331,14 @@ 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,
vtable_principal
.map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
target_principal,
) {
if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
== target_principal
{
return ControlFlow::Break(vptr_offset);
}
vptr_offset +=
tcx.own_existential_vtable_entries(vtable_principal.def_id()).len();
tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
if emit_vptr {
vptr_offset += 1;
@ -382,20 +364,27 @@ 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`.
let ty::Dynamic(target, _, _) = *target.kind() else {
bug!();
};
let target_principal = target.principal()?;
let target_principal = tcx.instantiate_bound_regions_with_erased(target.principal()?);
// Given that we have a target principal, it is a bug for there not to be a source principal.
let ty::Dynamic(source, _, _) = *source.kind() else {
bug!();
};
let source_principal =
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);
let source_principal = tcx.instantiate_bound_regions_with_erased(
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self),
);
let vtable_segment_callback = {
let mut vptr_offset = 0;
@ -406,13 +395,10 @@ 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,
vtable_principal
.map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
target_principal,
) {
tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
== target_principal
{
if emit_vptr {
return ControlFlow::Break(Some(vptr_offset));
} else {
@ -432,41 +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>,
hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>,
hr_target_principal: ty::PolyExistentialTraitRef<'tcx>,
) -> bool {
if hr_vtable_principal.def_id() != hr_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 hr_source_principal =
ocx.normalize(&ObligationCause::dummy(), param_env, hr_vtable_principal);
let hr_target_principal =
ocx.normalize(&ObligationCause::dummy(), param_env, hr_target_principal);
infcx.enter_forall(hr_target_principal, |target_principal| {
let source_principal = infcx.instantiate_binder_with_fresh_vars(
DUMMY_SP,
BoundRegionConversionTime::HigherRankedType,
hr_source_principal,
);
let Ok(()) = ocx.eq_trace(
&ObligationCause::dummy(),
param_env,
ToTrace::to_trace(&ObligationCause::dummy(), hr_target_principal, hr_source_principal),
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,

View file

@ -275,7 +275,7 @@ Here are some notable ones:
| `rustc_dump_def_parents` | Dumps the chain of `DefId` parents of certain definitions. |
| `rustc_dump_item_bounds` | Dumps the [`item_bounds`] of an item. |
| `rustc_dump_predicates` | Dumps the [`predicates_of`] an item. |
| `rustc_dump_vtable` | |
| `rustc_dump_vtable` | Dumps the vtable layout of an impl, or a type alias of a dyn type. |
| `rustc_hidden_type_of_opaques` | Dumps the [hidden type of each opaque types][opaq] in the crate. |
| `rustc_layout` | [See this section](#debugging-type-layouts). |
| `rustc_object_lifetime_default` | Dumps the [object lifetime defaults] of an item. |

View file

@ -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);
}

View file

@ -2,3 +2,5 @@ before
goodbye
before
goodbye
Hello!
Hello!

View file

@ -1,62 +0,0 @@
//@ check-pass
//@ compile-flags: -Z print-vtable-sizes
#![crate_type = "lib"]
trait A<T: help::V>: AsRef<[T::V]> + AsMut<[T::V]> {}
trait B<T>: AsRef<T> + AsRef<T> + AsRef<T> + AsRef<T> {}
trait C {
fn x() {} // not dyn-compatible, shouldn't be reported
}
// This does not have any upcasting cost,
// because both `Send` and `Sync` are traits
// with no methods
trait D: Send + Sync + help::MarkerWithSuper {}
// This can't have no cost without reordering,
// because `Super::f`.
trait E: help::MarkerWithSuper + Send + Sync {}
trait F {
fn a(&self);
fn b(&self);
fn c(&self);
fn d() -> Self
where
Self: Sized;
}
trait G: AsRef<u8> + AsRef<u16> + help::MarkerWithSuper {
fn a(&self);
fn b(&self);
fn c(&self);
fn d(&self);
fn e(&self);
fn f() -> Self
where
Self: Sized;
}
// Traits with the same name
const _: () = {
trait S {}
};
const _: () = {
trait S {}
};
mod help {
pub trait V {
type V;
}
pub trait MarkerWithSuper: Super {}
pub trait Super {
fn f(&self);
}
}

View file

@ -1,11 +0,0 @@
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "A", "entries": "6", "entries_ignoring_upcasting": "5", "entries_for_upcasting": "1", "upcasting_cost_percent": "20" }
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "G", "entries": "13", "entries_ignoring_upcasting": "11", "entries_for_upcasting": "2", "upcasting_cost_percent": "18.181818181818183" }
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "B", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "D", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "E", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "F", "entries": "6", "entries_ignoring_upcasting": "6", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "help::MarkerWithSuper", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "help::Super", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "help::V", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }

View file

@ -0,0 +1,27 @@
#![feature(rustc_attrs)]
// Test for <https://github.com/rust-lang/rust/issues/135316>.
trait Supertrait<T> {
fn _print_numbers(&self, mem: &[usize; 100]) {
}
}
impl<T> Supertrait<T> for () {}
trait Trait<T, U>: Supertrait<T> + Supertrait<U> {
fn say_hello(&self, _: &usize) {
}
}
impl<T, U> Trait<T, U> for () {}
// We should observe compatibility between these two vtables.
#[rustc_dump_vtable]
type First = dyn for<'a> Trait<&'static (), &'a ()>;
//~^ ERROR vtable entries
#[rustc_dump_vtable]
type Second = dyn Trait<&'static (), &'static ()>;
//~^ ERROR vtable entries
fn main() {}

View file

@ -0,0 +1,26 @@
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
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:20:1
|
LL | type First = dyn for<'a> Trait<&'static (), &'a ()>;
| ^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
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:24:1
|
LL | type Second = dyn Trait<&'static (), &'static ()>;
| ^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -0,0 +1,26 @@
//@ run-pass
//@ check-run-results
// Test for <https://github.com/rust-lang/rust/issues/135316>.
#![feature(trait_upcasting)]
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 () {}
fn main() {
(&() as &'static dyn for<'a> Trait<&'static (), &'a ()>
as &'static dyn Trait<&'static (), &'static ()>)
.say_hello(&0);
}

View file

@ -0,0 +1,37 @@
#![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:?}");
}
}
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> {}
#[rustc_dump_vtable]
impl Trait for () {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
type Virtual = dyn Middle<()>;
//~^ ERROR vtable entries
fn main() {}

View file

@ -0,0 +1,26 @@
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<() as Supertrait<()>>::_print_numbers),
Method(<() as Middle<()>>::say_hello),
]
--> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:30:1
|
LL | impl Trait for () {}
| ^^^^^^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
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:34:1
|
LL | type Virtual = dyn Middle<()>;
| ^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -0,0 +1,34 @@
//@ run-pass
//@ check-run-results
#![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:?}");
}
}
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 () {}
fn main() {
(&() as &dyn Trait as &dyn Middle<()>).say_hello(&0);
}

View file

@ -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();
}

View file

@ -2,8 +2,7 @@
//
// This test makes sure that multiple marker (method-less) traits can reuse the
// same pointer for upcasting.
//
//@ build-fail
#![crate_type = "lib"]
#![feature(rustc_attrs)]
@ -17,17 +16,13 @@ trait T {
fn method(&self) {}
}
#[rustc_dump_vtable]
trait A: M0 + M1 + M2 + T {} //~ error: vtable entries for `<S as A>`:
trait A: M0 + M1 + M2 + T {}
#[rustc_dump_vtable]
trait B: M0 + M1 + T + M2 {} //~ error: vtable entries for `<S as B>`:
trait B: M0 + M1 + T + M2 {}
#[rustc_dump_vtable]
trait C: M0 + T + M1 + M2 {} //~ error: vtable entries for `<S as C>`:
trait C: M0 + T + M1 + M2 {}
#[rustc_dump_vtable]
trait D: T + M0 + M1 + M2 {} //~ error: vtable entries for `<S as D>`:
trait D: T + M0 + M1 + M2 {}
struct S;
@ -35,13 +30,21 @@ impl M0 for S {}
impl M1 for S {}
impl M2 for S {}
impl T for S {}
#[rustc_dump_vtable]
impl A for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl B for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl C for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl D for S {}
//~^ ERROR vtable entries
pub fn require_vtables() {
fn require_vtables(_: &dyn A, _: &dyn B, _: &dyn C, _: &dyn D) {}
require_vtables(&S, &S, &S, &S)
}
fn main() {}

View file

@ -1,46 +1,46 @@
error: vtable entries for `<S as A>`: [
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as T>::method),
]
--> $DIR/multiple-markers.rs:21:1
--> $DIR/multiple-markers.rs:35:1
|
LL | trait A: M0 + M1 + M2 + T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
LL | impl A for S {}
| ^^^^^^^^^^^^
error: vtable entries for `<S as B>`: [
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as T>::method),
]
--> $DIR/multiple-markers.rs:24:1
--> $DIR/multiple-markers.rs:39:1
|
LL | trait B: M0 + M1 + T + M2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
LL | impl B for S {}
| ^^^^^^^^^^^^
error: vtable entries for `<S as C>`: [
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as T>::method),
]
--> $DIR/multiple-markers.rs:27:1
--> $DIR/multiple-markers.rs:43:1
|
LL | trait C: M0 + T + M1 + M2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
LL | impl C for S {}
| ^^^^^^^^^^^^
error: vtable entries for `<S as D>`: [
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as T>::method),
]
--> $DIR/multiple-markers.rs:30:1
--> $DIR/multiple-markers.rs:47:1
|
LL | trait D: T + M0 + M1 + M2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
LL | impl D for S {}
| ^^^^^^^^^^^^
error: aborting due to 4 previous errors

View file

@ -1,44 +1,37 @@
//@ build-fail
#![feature(rustc_attrs)]
#[rustc_dump_vtable]
trait A {
fn foo_a(&self) {}
}
#[rustc_dump_vtable]
trait B: A {
fn foo_b(&self) {}
}
#[rustc_dump_vtable]
trait C: A {
//~^ error vtable
fn foo_c(&self) {}
}
#[rustc_dump_vtable]
trait D: B + C {
//~^ error vtable
fn foo_d(&self) {}
}
struct S;
#[rustc_dump_vtable]
impl A for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl B for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl C for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl D for S {}
//~^ ERROR vtable entries
fn foo(d: &dyn D) {
d.foo_d();
}
fn bar(d: &dyn C) {
d.foo_c();
}
fn main() {
foo(&S);
bar(&S);
}
fn main() {}

View file

@ -1,4 +1,39 @@
error: vtable entries for `<S as D>`: [
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
]
--> $DIR/vtable-diamond.rs:22:1
|
LL | impl A for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
Method(<S as B>::foo_b),
]
--> $DIR/vtable-diamond.rs:26:1
|
LL | impl B for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
Method(<S as C>::foo_c),
]
--> $DIR/vtable-diamond.rs:30:1
|
LL | impl C for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
@ -8,22 +43,10 @@ error: vtable entries for `<S as D>`: [
TraitVPtr(<S as C>),
Method(<S as D>::foo_d),
]
--> $DIR/vtable-diamond.rs:21:1
--> $DIR/vtable-diamond.rs:34:1
|
LL | trait D: B + C {
| ^^^^^^^^^^^^^^
LL | impl D for S {}
| ^^^^^^^^^^^^
error: vtable entries for `<S as C>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
Method(<S as C>::foo_c),
]
--> $DIR/vtable-diamond.rs:15:1
|
LL | trait C: A {
| ^^^^^^^^^^
error: aborting due to 2 previous errors
error: aborting due to 4 previous errors

View file

@ -1,15 +1,16 @@
//@ build-fail
#![feature(rustc_attrs)]
// Ensure that dyn-incompatible methods in Iterator does not generate
// vtable entries.
#[rustc_dump_vtable]
trait A: Iterator {}
//~^ error vtable
impl<T> A for T where T: Iterator {}
#[rustc_dump_vtable]
type Test = dyn A<Item=u8>;
//~^ error vtable
fn foo(_a: &mut dyn A<Item=u8>) {
}

View file

@ -1,16 +1,16 @@
error: vtable entries for `<std::vec::IntoIter<u8> as A>`: [
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<std::vec::IntoIter<u8> as Iterator>::next),
Method(<std::vec::IntoIter<u8> as Iterator>::size_hint),
Method(<std::vec::IntoIter<u8> as Iterator>::advance_by),
Method(<std::vec::IntoIter<u8> as Iterator>::nth),
Method(<dyn A<Item = u8> as Iterator>::next - shim(reify)),
Method(<dyn A<Item = u8> as Iterator>::size_hint - shim(reify)),
Method(<dyn A<Item = u8> as Iterator>::advance_by - shim(reify)),
Method(<dyn A<Item = u8> as Iterator>::nth - shim(reify)),
]
--> $DIR/vtable-dyn-incompatible.rs:8:1
--> $DIR/vtable-dyn-incompatible.rs:11:1
|
LL | trait A: Iterator {}
| ^^^^^^^^^^^^^^^^^
LL | type Test = dyn A<Item=u8>;
| ^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -1,4 +1,3 @@
//@ build-fail
#![feature(rustc_attrs)]
// O --> G --> C --> A
@ -10,134 +9,126 @@
// |-> M --> K
// \-> L
#[rustc_dump_vtable]
trait A {
//~^ error vtable
fn foo_a(&self) {}
}
#[rustc_dump_vtable]
trait B {
//~^ error vtable
fn foo_b(&self) {}
}
#[rustc_dump_vtable]
trait C: A + B {
//~^ error vtable
fn foo_c(&self) {}
}
#[rustc_dump_vtable]
trait D {
//~^ error vtable
fn foo_d(&self) {}
}
#[rustc_dump_vtable]
trait E {
//~^ error vtable
fn foo_e(&self) {}
}
#[rustc_dump_vtable]
trait F: D + E {
//~^ error vtable
fn foo_f(&self) {}
}
#[rustc_dump_vtable]
trait G: C + F {
fn foo_g(&self) {}
}
#[rustc_dump_vtable]
trait H {
//~^ error vtable
fn foo_h(&self) {}
}
#[rustc_dump_vtable]
trait I {
//~^ error vtable
fn foo_i(&self) {}
}
#[rustc_dump_vtable]
trait J: H + I {
//~^ error vtable
fn foo_j(&self) {}
}
#[rustc_dump_vtable]
trait K {
//~^ error vtable
fn foo_k(&self) {}
}
#[rustc_dump_vtable]
trait L {
//~^ error vtable
fn foo_l(&self) {}
}
#[rustc_dump_vtable]
trait M: K + L {
//~^ error vtable
fn foo_m(&self) {}
}
#[rustc_dump_vtable]
trait N: J + M {
//~^ error vtable
fn foo_n(&self) {}
}
#[rustc_dump_vtable]
trait O: G + N {
//~^ error vtable
fn foo_o(&self) {}
}
struct S;
#[rustc_dump_vtable]
impl A for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl B for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl C for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl D for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl E for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl F for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl G for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl H for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl I for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl J for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl K for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl L for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl M for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl N for S {}
//~^ ERROR vtable entries
#[rustc_dump_vtable]
impl O for S {}
//~^ ERROR vtable entries
macro_rules! monomorphize_vtable {
($trait:ident) => {{
fn foo(_ : &dyn $trait) {}
foo(&S);
}}
}
fn main() {
monomorphize_vtable!(O);
monomorphize_vtable!(A);
monomorphize_vtable!(B);
monomorphize_vtable!(C);
monomorphize_vtable!(D);
monomorphize_vtable!(E);
monomorphize_vtable!(F);
monomorphize_vtable!(H);
monomorphize_vtable!(I);
monomorphize_vtable!(J);
monomorphize_vtable!(K);
monomorphize_vtable!(L);
monomorphize_vtable!(M);
monomorphize_vtable!(N);
}
fn main() {}

View file

@ -1,4 +1,190 @@
error: vtable entries for `<S as O>`: [
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
]
--> $DIR/vtable-multi-level.rs:75:1
|
LL | impl A for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as B>::foo_b),
]
--> $DIR/vtable-multi-level.rs:79:1
|
LL | impl B for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
Method(<S as B>::foo_b),
TraitVPtr(<S as B>),
Method(<S as C>::foo_c),
]
--> $DIR/vtable-multi-level.rs:83:1
|
LL | impl C for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as D>::foo_d),
]
--> $DIR/vtable-multi-level.rs:87:1
|
LL | impl D for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as E>::foo_e),
]
--> $DIR/vtable-multi-level.rs:91:1
|
LL | impl E for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as D>::foo_d),
Method(<S as E>::foo_e),
TraitVPtr(<S as E>),
Method(<S as F>::foo_f),
]
--> $DIR/vtable-multi-level.rs:95:1
|
LL | impl F for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
Method(<S as B>::foo_b),
TraitVPtr(<S as B>),
Method(<S as C>::foo_c),
Method(<S as D>::foo_d),
TraitVPtr(<S as D>),
Method(<S as E>::foo_e),
TraitVPtr(<S as E>),
Method(<S as F>::foo_f),
TraitVPtr(<S as F>),
Method(<S as G>::foo_g),
]
--> $DIR/vtable-multi-level.rs:99:1
|
LL | impl G for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as H>::foo_h),
]
--> $DIR/vtable-multi-level.rs:103:1
|
LL | impl H for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as I>::foo_i),
]
--> $DIR/vtable-multi-level.rs:107:1
|
LL | impl I for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as H>::foo_h),
Method(<S as I>::foo_i),
TraitVPtr(<S as I>),
Method(<S as J>::foo_j),
]
--> $DIR/vtable-multi-level.rs:111:1
|
LL | impl J for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as K>::foo_k),
]
--> $DIR/vtable-multi-level.rs:115:1
|
LL | impl K for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as L>::foo_l),
]
--> $DIR/vtable-multi-level.rs:119:1
|
LL | impl L for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as K>::foo_k),
Method(<S as L>::foo_l),
TraitVPtr(<S as L>),
Method(<S as M>::foo_m),
]
--> $DIR/vtable-multi-level.rs:123:1
|
LL | impl M for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as H>::foo_h),
Method(<S as I>::foo_i),
TraitVPtr(<S as I>),
Method(<S as J>::foo_j),
Method(<S as K>::foo_k),
TraitVPtr(<S as K>),
Method(<S as L>::foo_l),
TraitVPtr(<S as L>),
Method(<S as M>::foo_m),
TraitVPtr(<S as M>),
Method(<S as N>::foo_n),
]
--> $DIR/vtable-multi-level.rs:127:1
|
LL | impl N for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
@ -29,175 +215,10 @@ error: vtable entries for `<S as O>`: [
TraitVPtr(<S as N>),
Method(<S as O>::foo_o),
]
--> $DIR/vtable-multi-level.rs:97:1
--> $DIR/vtable-multi-level.rs:131:1
|
LL | trait O: G + N {
| ^^^^^^^^^^^^^^
LL | impl O for S {}
| ^^^^^^^^^^^^
error: vtable entries for `<S as A>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
]
--> $DIR/vtable-multi-level.rs:14:1
|
LL | trait A {
| ^^^^^^^
error: vtable entries for `<S as B>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as B>::foo_b),
]
--> $DIR/vtable-multi-level.rs:20:1
|
LL | trait B {
| ^^^^^^^
error: vtable entries for `<S as C>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
Method(<S as B>::foo_b),
TraitVPtr(<S as B>),
Method(<S as C>::foo_c),
]
--> $DIR/vtable-multi-level.rs:26:1
|
LL | trait C: A + B {
| ^^^^^^^^^^^^^^
error: vtable entries for `<S as D>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as D>::foo_d),
]
--> $DIR/vtable-multi-level.rs:32:1
|
LL | trait D {
| ^^^^^^^
error: vtable entries for `<S as E>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as E>::foo_e),
]
--> $DIR/vtable-multi-level.rs:38:1
|
LL | trait E {
| ^^^^^^^
error: vtable entries for `<S as F>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as D>::foo_d),
Method(<S as E>::foo_e),
TraitVPtr(<S as E>),
Method(<S as F>::foo_f),
]
--> $DIR/vtable-multi-level.rs:44:1
|
LL | trait F: D + E {
| ^^^^^^^^^^^^^^
error: vtable entries for `<S as H>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as H>::foo_h),
]
--> $DIR/vtable-multi-level.rs:55:1
|
LL | trait H {
| ^^^^^^^
error: vtable entries for `<S as I>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as I>::foo_i),
]
--> $DIR/vtable-multi-level.rs:61:1
|
LL | trait I {
| ^^^^^^^
error: vtable entries for `<S as J>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as H>::foo_h),
Method(<S as I>::foo_i),
TraitVPtr(<S as I>),
Method(<S as J>::foo_j),
]
--> $DIR/vtable-multi-level.rs:67:1
|
LL | trait J: H + I {
| ^^^^^^^^^^^^^^
error: vtable entries for `<S as K>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as K>::foo_k),
]
--> $DIR/vtable-multi-level.rs:73:1
|
LL | trait K {
| ^^^^^^^
error: vtable entries for `<S as L>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as L>::foo_l),
]
--> $DIR/vtable-multi-level.rs:79:1
|
LL | trait L {
| ^^^^^^^
error: vtable entries for `<S as M>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as K>::foo_k),
Method(<S as L>::foo_l),
TraitVPtr(<S as L>),
Method(<S as M>::foo_m),
]
--> $DIR/vtable-multi-level.rs:85:1
|
LL | trait M: K + L {
| ^^^^^^^^^^^^^^
error: vtable entries for `<S as N>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as H>::foo_h),
Method(<S as I>::foo_i),
TraitVPtr(<S as I>),
Method(<S as J>::foo_j),
Method(<S as K>::foo_k),
TraitVPtr(<S as K>),
Method(<S as L>::foo_l),
TraitVPtr(<S as L>),
Method(<S as M>::foo_m),
TraitVPtr(<S as M>),
Method(<S as N>::foo_n),
]
--> $DIR/vtable-multi-level.rs:91:1
|
LL | trait N: J + M {
| ^^^^^^^^^^^^^^
error: aborting due to 14 previous errors
error: aborting due to 15 previous errors

View file

@ -1,33 +1,29 @@
//@ build-fail
#![feature(rustc_attrs)]
#[rustc_dump_vtable]
trait A {
fn foo_a(&self) {}
}
#[rustc_dump_vtable]
trait B {
//~^ error vtable
fn foo_b(&self) {}
}
#[rustc_dump_vtable]
trait C: A + B {
//~^ error vtable
fn foo_c(&self) {}
}
struct S;
#[rustc_dump_vtable]
impl A for S {}
//~^ error vtable
#[rustc_dump_vtable]
impl B for S {}
//~^ error vtable
#[rustc_dump_vtable]
impl C for S {}
//~^ error vtable
fn foo(c: &dyn C) {}
fn bar(c: &dyn B) {}
fn main() {
foo(&S);
bar(&S);
}
fn main() {}

View file

@ -1,4 +1,26 @@
error: vtable entries for `<S as C>`: [
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
]
--> $DIR/vtable-multiple.rs:18:1
|
LL | impl A for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as B>::foo_b),
]
--> $DIR/vtable-multiple.rs:22:1
|
LL | impl B for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
@ -7,21 +29,10 @@ error: vtable entries for `<S as C>`: [
TraitVPtr(<S as B>),
Method(<S as C>::foo_c),
]
--> $DIR/vtable-multiple.rs:16:1
--> $DIR/vtable-multiple.rs:26:1
|
LL | trait C: A + B {
| ^^^^^^^^^^^^^^
LL | impl C for S {}
| ^^^^^^^^^^^^
error: vtable entries for `<S as B>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as B>::foo_b),
]
--> $DIR/vtable-multiple.rs:10:1
|
LL | trait B {
| ^^^^^^^
error: aborting due to 2 previous errors
error: aborting due to 3 previous errors

View file

@ -1,18 +1,14 @@
//@ build-fail
#![feature(rustc_attrs)]
#![feature(negative_impls)]
// B --> A
#[rustc_dump_vtable]
trait A {
fn foo_a1(&self) {}
fn foo_a2(&self) where Self: Send {}
}
#[rustc_dump_vtable]
trait B: A {
//~^ error vtable
fn foo_b1(&self) {}
fn foo_b2(&self) where Self: Send {}
}
@ -20,11 +16,12 @@ trait B: A {
struct S;
impl !Send for S {}
#[rustc_dump_vtable]
impl A for S {}
//~^ error vtable
#[rustc_dump_vtable]
impl B for S {}
//~^ error vtable
fn foo(_: &dyn B) {}
fn main() {
foo(&S);
}
fn main() {}

View file

@ -1,4 +1,16 @@
error: vtable entries for `<S as B>`: [
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a1),
Vacant,
]
--> $DIR/vtable-vacant.rs:20:1
|
LL | impl A for S {}
| ^^^^^^^^^^^^
error: vtable entries: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
@ -7,10 +19,10 @@ error: vtable entries for `<S as B>`: [
Method(<S as B>::foo_b1),
Vacant,
]
--> $DIR/vtable-vacant.rs:14:1
--> $DIR/vtable-vacant.rs:24:1
|
LL | trait B: A {
| ^^^^^^^^^^
LL | impl B for S {}
| ^^^^^^^^^^^^
error: aborting due to 1 previous error
error: aborting due to 2 previous errors