From b47166b2a85f2b41f3ddc90d6d0db6169e2ca467 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 10 Jan 2025 20:09:10 +0000 Subject: [PATCH 1/6] Rework rustc_dump_vtable --- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- .../rustc_hir_analysis/src/collect/dump.rs | 82 +++++++- compiler/rustc_hir_analysis/src/lib.rs | 13 +- compiler/rustc_trait_selection/messages.ftl | 2 - compiler/rustc_trait_selection/src/errors.rs | 11 +- .../src/traits/vtable.rs | 17 +- .../rustc-dev-guide/src/compiler-debugging.md | 2 +- ...ltiple-supertraits-modulo-binder-vtable.rs | 25 +++ ...le-supertraits-modulo-binder-vtable.stderr | 28 +++ ...supertraits-modulo-normalization-vtable.rs | 36 ++++ ...rtraits-modulo-normalization-vtable.stderr | 28 +++ tests/ui/traits/vtable/multiple-markers.rs | 27 ++- .../ui/traits/vtable/multiple-markers.stderr | 32 +-- tests/ui/traits/vtable/vtable-diamond.rs | 18 +- tests/ui/traits/vtable/vtable-diamond.stderr | 45 +++- .../traits/vtable/vtable-dyn-incompatible.rs | 5 +- .../vtable/vtable-dyn-incompatible.stderr | 16 +- tests/ui/traits/vtable/vtable-multi-level.rs | 74 ++++--- .../traits/vtable/vtable-multi-level.stderr | 197 ++++++++++-------- tests/ui/traits/vtable/vtable-multiple.rs | 14 +- tests/ui/traits/vtable/vtable-multiple.stderr | 33 ++- tests/ui/traits/vtable/vtable-vacant.rs | 9 +- tests/ui/traits/vtable/vtable-vacant.stderr | 22 +- 23 files changed, 504 insertions(+), 234 deletions(-) create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index c28a4360f6f91..c1ca0a7240076 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1143,7 +1143,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*/), diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index f1022d9575315..d990d824200a9 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -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(ty::Binder::dummy(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( + principal.map_bound(|principal| 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:#?}")); + } +} diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index a42a168234f07..0b6b4a0ce7036 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -151,11 +151,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. diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index b82bb27eb795a..502c2696bb312 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -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 diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 53a4e5031c6dd..1454d642bd258 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -13,7 +13,7 @@ use rustc_hir::intravisit::{Visitor, walk_ty}; use rustc_hir::{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; @@ -23,15 +23,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> { diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index b5bc8364c7b69..72ddf01c7913e 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -11,11 +11,10 @@ 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}; #[derive(Clone, Debug)] @@ -192,15 +191,6 @@ fn maybe_iter(i: Option) -> impl Iterator { 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() } @@ -317,11 +307,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) } diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index a2a6c8085df58..e2097b26e5c83 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -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. | diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs new file mode 100644 index 0000000000000..b41cf2e9f2679 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs @@ -0,0 +1,25 @@ +#![feature(rustc_attrs)] + +trait Supertrait { + fn _print_numbers(&self, mem: &[usize; 100]) { + } +} +impl Supertrait for () {} + +trait Trait: Supertrait + Supertrait { + fn say_hello(&self, _: &usize) { + } +} +impl Trait 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() {} diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr new file mode 100644 index 0000000000000..1b99be4ab8809 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr @@ -0,0 +1,28 @@ +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)), + Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)), + TraitVPtr(for<'a> Trait<&(), &'a ()> as Supertrait<&'a ()>>), + Method( Trait<&(), &'a ()> as Trait<&(), &()>>::say_hello - shim(reify)), + ] + --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:18:1 + | +LL | type First = dyn for<'a> Trait<&'static (), &'a ()>; + | ^^^^^^^^^^ + +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method( as Supertrait<&()>>::_print_numbers - shim(reify)), + Method( as Trait<&(), &()>>::say_hello - shim(reify)), + ] + --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:22:1 + | +LL | type Second = dyn Trait<&'static (), &'static ()>; + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs new file mode 100644 index 0000000000000..22d6bf94d8706 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs @@ -0,0 +1,36 @@ +#![feature(rustc_attrs)] + +#![feature(trait_upcasting)] + +trait Supertrait { + fn _print_numbers(&self, mem: &[usize; 100]) { + println!("{mem:?}"); + } +} +impl Supertrait for () {} + +trait Identity { + type Selff; +} +impl Identity for Selff { + type Selff = Selff; +} + +trait Middle: Supertrait<()> + Supertrait { + fn say_hello(&self, _: &usize) { + println!("Hello!"); + } +} +impl Middle 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() {} diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr new file mode 100644 index 0000000000000..9e93e1c48c9e7 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr @@ -0,0 +1,28 @@ +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(<() as Supertrait<()>>::_print_numbers), + Method(<() as Supertrait<()>>::_print_numbers), + TraitVPtr(<() as Supertrait<<() as Identity>::Selff>>), + Method(<() as Middle<()>>::say_hello), + ] + --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:29:1 + | +LL | impl Trait for () {} + | ^^^^^^^^^^^^^^^^^ + +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method( as Supertrait<()>>::_print_numbers - shim(reify)), + Method( as Middle<()>>::say_hello - shim(reify)), + ] + --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:33:1 + | +LL | type Virtual = dyn Middle<()>; + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/traits/vtable/multiple-markers.rs b/tests/ui/traits/vtable/multiple-markers.rs index 8a9e7a006cf55..e19d9e9b31b61 100644 --- a/tests/ui/traits/vtable/multiple-markers.rs +++ b/tests/ui/traits/vtable/multiple-markers.rs @@ -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 ``: +trait A: M0 + M1 + M2 + T {} -#[rustc_dump_vtable] -trait B: M0 + M1 + T + M2 {} //~ error: vtable entries for ``: +trait B: M0 + M1 + T + M2 {} -#[rustc_dump_vtable] -trait C: M0 + T + M1 + M2 {} //~ error: vtable entries for ``: +trait C: M0 + T + M1 + M2 {} -#[rustc_dump_vtable] -trait D: T + M0 + M1 + M2 {} //~ error: vtable entries for ``: +trait D: T + M0 + M1 + M2 {} struct S; @@ -35,10 +30,22 @@ 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) {} diff --git a/tests/ui/traits/vtable/multiple-markers.stderr b/tests/ui/traits/vtable/multiple-markers.stderr index 36ac8b24eb530..35dd3c2516d8b 100644 --- a/tests/ui/traits/vtable/multiple-markers.stderr +++ b/tests/ui/traits/vtable/multiple-markers.stderr @@ -1,46 +1,46 @@ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::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 ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::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 ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::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 ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::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 diff --git a/tests/ui/traits/vtable/vtable-diamond.rs b/tests/ui/traits/vtable/vtable-diamond.rs index 2cfa86c526de3..10e5afa6b352b 100644 --- a/tests/ui/traits/vtable/vtable-diamond.rs +++ b/tests/ui/traits/vtable/vtable-diamond.rs @@ -1,34 +1,38 @@ -//@ 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(); diff --git a/tests/ui/traits/vtable/vtable-diamond.stderr b/tests/ui/traits/vtable/vtable-diamond.stderr index f3718c5d852f0..4644bf339b17d 100644 --- a/tests/ui/traits/vtable/vtable-diamond.stderr +++ b/tests/ui/traits/vtable/vtable-diamond.stderr @@ -1,29 +1,52 @@ -error: vtable entries for ``: [ +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(::foo_a), + ] + --> $DIR/vtable-diamond.rs:22:1 + | +LL | impl A for S {} + | ^^^^^^^^^^^^ + +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), Method(::foo_b), + ] + --> $DIR/vtable-diamond.rs:26:1 + | +LL | impl B for S {} + | ^^^^^^^^^^^^ + +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(::foo_a), Method(::foo_c), - TraitVPtr(), - Method(::foo_d), ] - --> $DIR/vtable-diamond.rs:21:1 + --> $DIR/vtable-diamond.rs:30:1 | -LL | trait D: B + C { - | ^^^^^^^^^^^^^^ +LL | impl C for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), + Method(::foo_b), Method(::foo_c), + TraitVPtr(), + Method(::foo_d), ] - --> $DIR/vtable-diamond.rs:15:1 + --> $DIR/vtable-diamond.rs:34:1 | -LL | trait C: A { - | ^^^^^^^^^^ +LL | impl D for S {} + | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/traits/vtable/vtable-dyn-incompatible.rs b/tests/ui/traits/vtable/vtable-dyn-incompatible.rs index 64a8138bdcfb3..5bf2626859681 100644 --- a/tests/ui/traits/vtable/vtable-dyn-incompatible.rs +++ b/tests/ui/traits/vtable/vtable-dyn-incompatible.rs @@ -1,13 +1,14 @@ -//@ build-fail #![feature(rustc_attrs)] // Ensure that dyn-incompatible methods in Iterator does not generate // vtable entries. #[rustc_dump_vtable] -trait A: Iterator {} +type Test = dyn A; //~^ error vtable +trait A: Iterator {} + impl A for T where T: Iterator {} fn foo(_a: &mut dyn A) { diff --git a/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr b/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr index e442c3eac00ea..6140ab53257dd 100644 --- a/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr +++ b/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr @@ -1,16 +1,16 @@ -error: vtable entries for ` as A>`: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, - Method( as Iterator>::next), - Method( as Iterator>::size_hint), - Method( as Iterator>::advance_by), - Method( as Iterator>::nth), + Method( as Iterator>::next - shim(reify)), + Method( as Iterator>::size_hint - shim(reify)), + Method( as Iterator>::advance_by - shim(reify)), + Method( as Iterator>::nth - shim(reify)), ] - --> $DIR/vtable-dyn-incompatible.rs:8:1 + --> $DIR/vtable-dyn-incompatible.rs:7:1 | -LL | trait A: Iterator {} - | ^^^^^^^^^^^^^^^^^ +LL | type Test = dyn A; + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/vtable/vtable-multi-level.rs b/tests/ui/traits/vtable/vtable-multi-level.rs index bedbf84d30398..78197e90a3741 100644 --- a/tests/ui/traits/vtable/vtable-multi-level.rs +++ b/tests/ui/traits/vtable/vtable-multi-level.rs @@ -1,4 +1,3 @@ -//@ build-fail #![feature(rustc_attrs)] // O --> G --> C --> A @@ -10,112 +9,127 @@ // |-> 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) => {{ diff --git a/tests/ui/traits/vtable/vtable-multi-level.stderr b/tests/ui/traits/vtable/vtable-multi-level.stderr index c4389e23fc10f..961900aa3d291 100644 --- a/tests/ui/traits/vtable/vtable-multi-level.stderr +++ b/tests/ui/traits/vtable/vtable-multi-level.stderr @@ -1,134 +1,119 @@ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), - Method(::foo_b), - TraitVPtr(), - Method(::foo_c), - Method(::foo_d), - TraitVPtr(), - Method(::foo_e), - TraitVPtr(), - Method(::foo_f), - TraitVPtr(), - Method(::foo_g), - Method(::foo_h), - TraitVPtr(), - Method(::foo_i), - TraitVPtr(), - Method(::foo_j), - TraitVPtr(), - Method(::foo_k), - TraitVPtr(), - Method(::foo_l), - TraitVPtr(), - Method(::foo_m), - TraitVPtr(), - Method(::foo_n), - TraitVPtr(), - Method(::foo_o), ] - --> $DIR/vtable-multi-level.rs:97:1 + --> $DIR/vtable-multi-level.rs:75:1 | -LL | trait O: G + N { - | ^^^^^^^^^^^^^^ +LL | impl A for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, - Method(::foo_a), + Method(::foo_b), ] - --> $DIR/vtable-multi-level.rs:14:1 + --> $DIR/vtable-multi-level.rs:79:1 | -LL | trait A { - | ^^^^^^^ +LL | impl B for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, + Method(::foo_a), Method(::foo_b), + TraitVPtr(), + Method(::foo_c), ] - --> $DIR/vtable-multi-level.rs:20:1 + --> $DIR/vtable-multi-level.rs:83:1 | -LL | trait B { - | ^^^^^^^ +LL | impl C for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, - Method(::foo_a), - Method(::foo_b), - TraitVPtr(), - Method(::foo_c), + Method(::foo_d), ] - --> $DIR/vtable-multi-level.rs:26:1 + --> $DIR/vtable-multi-level.rs:87:1 | -LL | trait C: A + B { - | ^^^^^^^^^^^^^^ +LL | impl D for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, - Method(::foo_d), + Method(::foo_e), ] - --> $DIR/vtable-multi-level.rs:32:1 + --> $DIR/vtable-multi-level.rs:91:1 | -LL | trait D { - | ^^^^^^^ +LL | impl E for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, + Method(::foo_d), Method(::foo_e), + TraitVPtr(), + Method(::foo_f), ] - --> $DIR/vtable-multi-level.rs:38:1 + --> $DIR/vtable-multi-level.rs:95:1 | -LL | trait E { - | ^^^^^^^ +LL | impl F for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, + Method(::foo_a), + Method(::foo_b), + TraitVPtr(), + Method(::foo_c), Method(::foo_d), + TraitVPtr(), Method(::foo_e), TraitVPtr(), Method(::foo_f), + TraitVPtr(), + Method(::foo_g), ] - --> $DIR/vtable-multi-level.rs:44:1 + --> $DIR/vtable-multi-level.rs:99:1 | -LL | trait F: D + E { - | ^^^^^^^^^^^^^^ +LL | impl G for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_h), ] - --> $DIR/vtable-multi-level.rs:55:1 + --> $DIR/vtable-multi-level.rs:103:1 | -LL | trait H { - | ^^^^^^^ +LL | impl H for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_i), ] - --> $DIR/vtable-multi-level.rs:61:1 + --> $DIR/vtable-multi-level.rs:107:1 | -LL | trait I { - | ^^^^^^^ +LL | impl I for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, @@ -137,34 +122,34 @@ error: vtable entries for ``: [ TraitVPtr(), Method(::foo_j), ] - --> $DIR/vtable-multi-level.rs:67:1 + --> $DIR/vtable-multi-level.rs:111:1 | -LL | trait J: H + I { - | ^^^^^^^^^^^^^^ +LL | impl J for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_k), ] - --> $DIR/vtable-multi-level.rs:73:1 + --> $DIR/vtable-multi-level.rs:115:1 | -LL | trait K { - | ^^^^^^^ +LL | impl K for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_l), ] - --> $DIR/vtable-multi-level.rs:79:1 + --> $DIR/vtable-multi-level.rs:119:1 | -LL | trait L { - | ^^^^^^^ +LL | impl L for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, @@ -173,12 +158,12 @@ error: vtable entries for ``: [ TraitVPtr(), Method(::foo_m), ] - --> $DIR/vtable-multi-level.rs:85:1 + --> $DIR/vtable-multi-level.rs:123:1 | -LL | trait M: K + L { - | ^^^^^^^^^^^^^^ +LL | impl M for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, @@ -194,10 +179,46 @@ error: vtable entries for ``: [ TraitVPtr(), Method(::foo_n), ] - --> $DIR/vtable-multi-level.rs:91:1 + --> $DIR/vtable-multi-level.rs:127:1 + | +LL | impl N for S {} + | ^^^^^^^^^^^^ + +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(::foo_a), + Method(::foo_b), + TraitVPtr(), + Method(::foo_c), + Method(::foo_d), + TraitVPtr(), + Method(::foo_e), + TraitVPtr(), + Method(::foo_f), + TraitVPtr(), + Method(::foo_g), + Method(::foo_h), + TraitVPtr(), + Method(::foo_i), + TraitVPtr(), + Method(::foo_j), + TraitVPtr(), + Method(::foo_k), + TraitVPtr(), + Method(::foo_l), + TraitVPtr(), + Method(::foo_m), + TraitVPtr(), + Method(::foo_n), + TraitVPtr(), + Method(::foo_o), + ] + --> $DIR/vtable-multi-level.rs:131:1 | -LL | trait N: J + M { - | ^^^^^^^^^^^^^^ +LL | impl O for S {} + | ^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/traits/vtable/vtable-multiple.rs b/tests/ui/traits/vtable/vtable-multiple.rs index beaaf4db6b1c7..14a355bca0a19 100644 --- a/tests/ui/traits/vtable/vtable-multiple.rs +++ b/tests/ui/traits/vtable/vtable-multiple.rs @@ -1,28 +1,30 @@ -//@ 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) {} diff --git a/tests/ui/traits/vtable/vtable-multiple.stderr b/tests/ui/traits/vtable/vtable-multiple.stderr index 0dcd84433090c..bae44148baa87 100644 --- a/tests/ui/traits/vtable/vtable-multiple.stderr +++ b/tests/ui/traits/vtable/vtable-multiple.stderr @@ -1,27 +1,38 @@ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), + ] + --> $DIR/vtable-multiple.rs:18:1 + | +LL | impl A for S {} + | ^^^^^^^^^^^^ + +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, Method(::foo_b), - TraitVPtr(), - Method(::foo_c), ] - --> $DIR/vtable-multiple.rs:16:1 + --> $DIR/vtable-multiple.rs:22:1 | -LL | trait C: A + B { - | ^^^^^^^^^^^^^^ +LL | impl B for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, + Method(::foo_a), Method(::foo_b), + TraitVPtr(), + Method(::foo_c), ] - --> $DIR/vtable-multiple.rs:10:1 + --> $DIR/vtable-multiple.rs:26:1 | -LL | trait B { - | ^^^^^^^ +LL | impl C for S {} + | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/traits/vtable/vtable-vacant.rs b/tests/ui/traits/vtable/vtable-vacant.rs index b3c7681570362..570c7aca35805 100644 --- a/tests/ui/traits/vtable/vtable-vacant.rs +++ b/tests/ui/traits/vtable/vtable-vacant.rs @@ -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,8 +16,13 @@ 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) {} diff --git a/tests/ui/traits/vtable/vtable-vacant.stderr b/tests/ui/traits/vtable/vtable-vacant.stderr index f6961ca010ed8..ed8466f7f2ecd 100644 --- a/tests/ui/traits/vtable/vtable-vacant.stderr +++ b/tests/ui/traits/vtable/vtable-vacant.stderr @@ -1,4 +1,16 @@ -error: vtable entries for ``: [ +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(::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 ``: [ Method(::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 From d3d141686efbd9674a1524a931e5c27dc4314906 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 10 Jan 2025 20:38:37 +0000 Subject: [PATCH 2/6] Remove print_vtable_sizes --- compiler/rustc_interface/src/passes.rs | 91 ------------------- compiler/rustc_session/src/code_stats.rs | 60 +----------- compiler/rustc_session/src/options.rs | 2 - tests/ui/traits/object/print_vtable_sizes.rs | 62 ------------- .../traits/object/print_vtable_sizes.stdout | 11 --- 5 files changed, 1 insertion(+), 225 deletions(-) delete mode 100644 tests/ui/traits/object/print_vtable_sizes.rs delete mode 100644 tests/ui/traits/object/print_vtable_sizes.stdout diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index aff66e48fbbee..d0dafca1cda97 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -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}; @@ -985,90 +984,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 + AsRef<()> { type T; } - // - // Without self type we can't normalize `Self::T`, so we can't know if `AsRef` - // 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 { fn f(&self) where T: Copy; } - // - // Here `dyn B` will have 4 entries, while `dyn B` 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::(()) - }); - - 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 @@ -1149,12 +1064,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 } diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index f3c21992784c0..b4597ae2515d4 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -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, } -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>, - vtable_sizes: Lock>, } 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::>(); - - // 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}" }}"# - ); - } - } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 3af6df6301774..de9dd14e980b1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2035,8 +2035,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, diff --git a/tests/ui/traits/object/print_vtable_sizes.rs b/tests/ui/traits/object/print_vtable_sizes.rs deleted file mode 100644 index 2b1745da5f308..0000000000000 --- a/tests/ui/traits/object/print_vtable_sizes.rs +++ /dev/null @@ -1,62 +0,0 @@ -//@ check-pass -//@ compile-flags: -Z print-vtable-sizes -#![crate_type = "lib"] - -trait A: AsRef<[T::V]> + AsMut<[T::V]> {} - -trait B: AsRef + AsRef + AsRef + AsRef {} - -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 + AsRef + 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); - } -} diff --git a/tests/ui/traits/object/print_vtable_sizes.stdout b/tests/ui/traits/object/print_vtable_sizes.stdout deleted file mode 100644 index 4daf476957657..0000000000000 --- a/tests/ui/traits/object/print_vtable_sizes.stdout +++ /dev/null @@ -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" } From b4b24570e031ab59c0d1d24d6e83660afec975fd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 10 Jan 2025 04:36:11 +0000 Subject: [PATCH 3/6] Do not treat vtable supertraits as distinct when bound with different bound vars --- .../rustc_codegen_cranelift/src/constant.rs | 5 +- compiler/rustc_codegen_gcc/src/common.rs | 7 +- compiler/rustc_codegen_llvm/src/common.rs | 7 +- .../src/debuginfo/metadata.rs | 2 +- compiler/rustc_codegen_ssa/src/meth.rs | 14 +- .../rustc_const_eval/src/interpret/cast.rs | 10 +- .../rustc_const_eval/src/interpret/traits.rs | 3 +- .../rustc_hir_analysis/src/collect/dump.rs | 4 +- compiler/rustc_middle/src/query/keys.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 6 +- compiler/rustc_middle/src/ty/layout.rs | 7 +- compiler/rustc_middle/src/ty/vtable.rs | 17 ++- compiler/rustc_monomorphize/src/collector.rs | 14 +- compiler/rustc_smir/src/rustc_smir/context.rs | 4 +- .../src/traits/vtable.rs | 132 ++++++++---------- ...le-supertraits-modulo-binder-vtable.stderr | 2 - .../multiple-supertraits-modulo-binder.rs | 24 ++++ ...tiple-supertraits-modulo-binder.run.stdout | 1 + 18 files changed, 146 insertions(+), 115 deletions(-) create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 3e7b81a96b68c..05ba53430a8a9 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -245,7 +245,10 @@ pub(crate) fn data_id_for_vtable<'tcx>( ty: Ty<'tcx>, trait_ref: Option>>, ) -> DataId { - let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + let alloc_id = tcx.vtable_allocation(( + ty, + trait_ref.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)), + )); data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not) } diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index f43743fc2a41b..2052a6aa21597 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -229,7 +229,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) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index adfe8aeb5c539..7fa9975565419 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -308,7 +308,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(init, alloc.inner().align, None); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 88e43e1c678ad..08d8f626c51ae 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1406,7 +1406,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>( let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref { let trait_ref = poly_trait_ref.with_self_ty(tcx, ty); - let trait_ref = tcx.erase_regions(trait_ref); + let trait_ref = tcx.erase_regions(tcx.instantiate_bound_regions_with_erased(trait_ref)); tcx.vtable_entries(trait_ref) } else { diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 64cd4c38937e7..c51a57be71cec 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -96,24 +96,28 @@ fn dyn_trait_in_self(ty: Ty<'_>) -> Option> { pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( cx: &Cx, ty: Ty<'tcx>, - trait_ref: Option>, + poly_trait_ref: Option>, ) -> Cx::Value { let tcx = cx.tcx(); // Check the cache. - if let Some(&val) = cx.vtables().borrow().get(&(ty, trait_ref)) { + if let Some(&val) = cx.vtables().borrow().get(&(ty, poly_trait_ref)) { return val; } + // FIXME(trait_upcasting): Take a non-higher-ranked vtable as arg. + let trait_ref = + poly_trait_ref.map(|trait_ref| tcx.instantiate_bound_regions_with_erased(trait_ref)); + let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref)); let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory(); let vtable_const = cx.const_data_from_alloc(vtable_allocation); let align = cx.data_layout().pointer_align.abi; let vtable = cx.static_addr_of(vtable_const, align, Some("vtable")); - cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable); - cx.create_vtable_debuginfo(ty, trait_ref, vtable); - cx.vtables().borrow_mut().insert((ty, trait_ref), vtable); + cx.apply_vcall_visibility_metadata(ty, poly_trait_ref, vtable); + cx.create_vtable_debuginfo(ty, poly_trait_ref, vtable); + cx.vtables().borrow_mut().insert((ty, poly_trait_ref), vtable); vtable } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index ef3e96784ce86..b05efd10e6666 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -419,8 +419,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { 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) + let Some(&ty::VtblEntry::TraitVPtr(_)) = vtable_entries.get(entry_idx) else { span_bug!( self.cur_span(), @@ -429,13 +428,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { 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)) - ); } 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 diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index af8d618b6b5e5..4cfaacebfcd0e 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -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 diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index d990d824200a9..4a508fc0cf6a6 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -123,7 +123,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { ); continue; }; - tcx.vtable_entries(ty::Binder::dummy(trait_ref)) + tcx.vtable_entries(trait_ref) } hir::ItemKind::TyAlias(_, _) => { let ty = tcx.type_of(def_id).instantiate_identity(); @@ -149,7 +149,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { }; if let Some(principal) = data.principal() { tcx.vtable_entries( - principal.map_bound(|principal| principal.with_self_ty(tcx, ty)), + tcx.instantiate_bound_regions_with_erased(principal).with_self_ty(tcx, ty), ) } else { TyCtxt::COMMON_VTABLE_ENTRIES diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index e243425c0b7ba..03fe0a300c081 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -95,7 +95,7 @@ impl<'tcx> Key for mir::interpret::GlobalId<'tcx> { } } -impl<'tcx> Key for (Ty<'tcx>, Option>) { +impl<'tcx> Key for (Ty<'tcx>, Option>) { type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index bfbcb0532c10e..bfa2fb89d9cb6 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1436,9 +1436,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 { @@ -1450,7 +1450,7 @@ rustc_queries! { key.1, key.0 } } - query vtable_allocation(key: (Ty<'tcx>, Option>)) -> mir::interpret::AllocId { + query vtable_allocation(key: (Ty<'tcx>, Option>)) -> mir::interpret::AllocId { desc { |tcx| "vtable const allocation for <{} as {}>", key.0, key.1.map(|trait_ref| format!("{trait_ref}")).unwrap_or("_".to_owned()) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 8d4d127607d34..a031703cea0dd 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -849,7 +849,12 @@ where } let mk_dyn_vtable = |principal: Option>| { - 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, diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 09a05104e490a..6d97b85d46b2c 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -4,7 +4,7 @@ use rustc_ast::Mutability; use rustc_macros::HashStable; use crate::mir::interpret::{AllocId, 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> { @@ -19,7 +19,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> { @@ -56,7 +56,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>, + trait_ref: Option>, ) -> usize { let mut count = TyCtxt::COMMON_VTABLE_ENTRIES.len(); let Some(trait_ref) = trait_ref else { @@ -64,7 +64,7 @@ pub(crate) fn vtable_min_entries<'tcx>( }; // This includes self in supertraits. - for def_id in tcx.supertrait_def_ids(trait_ref.def_id()) { + for def_id in tcx.supertrait_def_ids(trait_ref.def_id) { count += tcx.own_existential_vtable_entries(def_id).len(); } @@ -80,7 +80,7 @@ pub(crate) fn vtable_min_entries<'tcx>( /// initial contents.) pub(super) fn vtable_allocation_provider<'tcx>( tcx: TyCtxt<'tcx>, - key: (Ty<'tcx>, Option>), + key: (Ty<'tcx>, Option>), ) -> AllocId { let (ty, poly_trait_ref) = key; @@ -115,7 +115,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); @@ -131,13 +131,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) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index c2e9498908cda..1d8615eb950ee 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -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) } } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 9793a4d416239..172b75a444292 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -751,7 +751,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)) } diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 72ddf01c7913e..b23d1da160845 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -2,8 +2,8 @@ use std::fmt::Debug; use std::ops::ControlFlow; use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; 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; @@ -20,7 +20,7 @@ use crate::traits::{ObligationCtxt, 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 @@ -28,7 +28,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, ) -> Option { prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value() @@ -38,7 +38,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, ) -> ControlFlow { // The following constraints holds for the final arrangement. @@ -91,7 +91,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); @@ -124,10 +124,19 @@ 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() + Some( + tcx.instantiate_bound_regions_with_erased( + pred.instantiate_supertrait( + tcx, + ty::Binder::dummy(inner_most_trait_ref), + ) + .as_trait_clause()?, + ) + .trait_ref, + ) }); // Find an unvisited supertrait @@ -135,16 +144,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, @@ -153,8 +157,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, @@ -168,11 +171,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 @@ -229,7 +227,7 @@ 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!("vtable_entries({:?})", trait_ref); @@ -241,33 +239,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 @@ -318,10 +309,11 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe 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; @@ -333,15 +325,14 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe 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)), + 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; @@ -373,14 +364,15 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( 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; @@ -391,11 +383,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(); + 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)), + ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal), target_principal, ) { if emit_vptr { @@ -419,37 +410,32 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( fn trait_refs_are_compatible<'tcx>( tcx: TyCtxt<'tcx>, - hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>, - hr_target_principal: ty::PolyExistentialTraitRef<'tcx>, + vtable_principal: ty::ExistentialTraitRef<'tcx>, + target_principal: ty::ExistentialTraitRef<'tcx>, ) -> bool { - if hr_vtable_principal.def_id() != hr_target_principal.def_id() { + 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 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( + 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_trace( + &ObligationCause::dummy(), + param_env, + ToTrace::to_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() - }) + ty::Binder::dummy(target_principal), + ty::Binder::dummy(source_principal), + ), + target_principal, + source_principal, + ) else { + return false; + }; + ocx.select_all_or_error().is_empty() } pub(super) fn provide(providers: &mut Providers) { diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr index 1b99be4ab8809..4bb7e1bdb6ad2 100644 --- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr @@ -3,8 +3,6 @@ error: vtable entries: [ MetadataSize, MetadataAlign, Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)), - Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)), - TraitVPtr(for<'a> Trait<&(), &'a ()> as Supertrait<&'a ()>>), Method( Trait<&(), &'a ()> as Trait<&(), &()>>::say_hello - shim(reify)), ] --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:18:1 diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs new file mode 100644 index 0000000000000..7bc069f4f4407 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs @@ -0,0 +1,24 @@ +//@ run-pass +//@ check-run-results + +#![feature(trait_upcasting)] + +trait Supertrait { + fn _print_numbers(&self, mem: &[usize; 100]) { + println!("{mem:?}"); + } +} +impl Supertrait for () {} + +trait Trait: Supertrait + Supertrait { + fn say_hello(&self, _: &usize) { + println!("Hello!"); + } +} +impl Trait for () {} + +fn main() { + (&() as &'static dyn for<'a> Trait<&'static (), &'a ()> + as &'static dyn Trait<&'static (), &'static ()>) + .say_hello(&0); +} diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout new file mode 100644 index 0000000000000..10ddd6d257e01 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout @@ -0,0 +1 @@ +Hello! From 65dc13d904302945779ce0149636ad3adb589a7d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 10 Jan 2025 04:47:45 +0000 Subject: [PATCH 4/6] Normalize vtable entries before walking and deduplicating them --- .../rustc_const_eval/src/interpret/cast.rs | 9 +++++- compiler/rustc_infer/src/infer/at.rs | 24 ++++++++++++++ .../src/traits/vtable.rs | 19 ++++------- ...rtraits-modulo-normalization-vtable.stderr | 2 -- ...ltiple-supertraits-modulo-normalization.rs | 32 +++++++++++++++++++ ...upertraits-modulo-normalization.run.stdout | 1 + 6 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index b05efd10e6666..52bc2af928dce 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -419,7 +419,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { 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(_)) = vtable_entries.get(entry_idx) + let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) = + vtable_entries.get(entry_idx) else { span_bug!( self.cur_span(), @@ -428,6 +429,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dest_pointee_ty ); }; + 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 diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 12e2bbc968f0f..ad15b764bcc38 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -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), + )), + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index b23d1da160845..fb27051854e2d 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -3,7 +3,6 @@ use std::ops::ControlFlow; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::at::ToTrace; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::util::PredicateSet; use rustc_middle::bug; @@ -128,7 +127,8 @@ fn prepare_vtable_segments_inner<'tcx, T>( .iter_identity_copied() .filter_map(move |(pred, _)| { Some( - tcx.instantiate_bound_regions_with_erased( + tcx.normalize_erasing_late_bound_regions( + ty::TypingEnv::fully_monomorphized(), pred.instantiate_supertrait( tcx, ty::Binder::dummy(inner_most_trait_ref), @@ -229,6 +229,8 @@ fn vtable_entries<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, ) -> &'tcx [VtblEntry<'tcx>] { + debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param()); + debug!("vtable_entries({:?})", trait_ref); let mut entries = vec![]; @@ -422,17 +424,8 @@ fn trait_refs_are_compatible<'tcx>( 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_trace( - &ObligationCause::dummy(), - param_env, - ToTrace::to_trace( - &ObligationCause::dummy(), - ty::Binder::dummy(target_principal), - ty::Binder::dummy(source_principal), - ), - target_principal, - source_principal, - ) else { + let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, target_principal, source_principal) + else { return false; }; ocx.select_all_or_error().is_empty() diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr index 9e93e1c48c9e7..04b1afae7beca 100644 --- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr @@ -3,8 +3,6 @@ error: vtable entries: [ MetadataSize, MetadataAlign, Method(<() as Supertrait<()>>::_print_numbers), - Method(<() as Supertrait<()>>::_print_numbers), - TraitVPtr(<() as Supertrait<<() as Identity>::Selff>>), Method(<() as Middle<()>>::say_hello), ] --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:29:1 diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs new file mode 100644 index 0000000000000..fd0f62b4255a1 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs @@ -0,0 +1,32 @@ +//@ run-pass +//@ check-run-results + +#![feature(trait_upcasting)] + +trait Supertrait { + fn _print_numbers(&self, mem: &[usize; 100]) { + println!("{mem:?}"); + } +} +impl Supertrait for () {} + +trait Identity { + type Selff; +} +impl Identity for Selff { + type Selff = Selff; +} + +trait Middle: Supertrait<()> + Supertrait { + fn say_hello(&self, _: &usize) { + println!("Hello!"); + } +} +impl Middle for () {} + +trait Trait: Middle<<() as Identity>::Selff> {} +impl Trait for () {} + +fn main() { + (&() as &dyn Trait as &dyn Middle<()>).say_hello(&0); +} diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout new file mode 100644 index 0000000000000..10ddd6d257e01 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout @@ -0,0 +1 @@ +Hello! From 45f061b03641ca3132bfe2b30250cb0018cd408b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 10 Jan 2025 20:26:10 +0000 Subject: [PATCH 5/6] Use ExistentialTraitRef throughout codegen --- .../rustc_codegen_cranelift/src/constant.rs | 25 ++++++++++------- .../rustc_codegen_cranelift/src/unsize.rs | 7 ++++- .../rustc_codegen_cranelift/src/vtable.rs | 2 +- compiler/rustc_codegen_gcc/src/context.rs | 6 ++--- compiler/rustc_codegen_gcc/src/debuginfo.rs | 4 +-- compiler/rustc_codegen_llvm/src/context.rs | 8 +++--- .../src/debuginfo/metadata.rs | 12 ++++----- .../src/debuginfo/metadata/type_map.rs | 6 ++--- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 2 +- compiler/rustc_codegen_ssa/src/base.rs | 15 +++++------ .../src/debuginfo/type_names.rs | 6 ++--- compiler/rustc_codegen_ssa/src/meth.rs | 27 ++++++++++--------- .../rustc_codegen_ssa/src/traits/debuginfo.rs | 4 +-- compiler/rustc_codegen_ssa/src/traits/misc.rs | 4 +-- compiler/rustc_symbol_mangling/src/lib.rs | 2 +- compiler/rustc_symbol_mangling/src/v0.rs | 4 +-- 16 files changed, 71 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 05ba53430a8a9..425b2adf32a37 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -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,12 +245,9 @@ pub(crate) fn data_id_for_vtable<'tcx>( cx: &mut ConstantCx, module: &mut dyn Module, ty: Ty<'tcx>, - trait_ref: Option>>, + trait_ref: Option>, ) -> DataId { - let alloc_id = tcx.vtable_allocation(( - ty, - trait_ref.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)), - )); + let alloc_id = tcx.vtable_allocation((ty, trait_ref)); data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not) } @@ -463,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) { diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 2843e5bbdfb2c..f8bbb21492015 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -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), } } diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index 82b6178be9dcd..a460023b59cb6 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -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>, + trait_ref: Option>, ) -> 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); diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index c81c53359fd18..e97009aedccb2 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -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, Function<'gcc>>>, /// Cache generated vtables pub vtables: - RefCell, Option>), RValue<'gcc>>>, + RefCell, Option>), 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, Option>), RValue<'gcc>>> { + ) -> &RefCell, Option>), RValue<'gcc>>> { &self.vtables } diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index d3aeb7f3bdeb3..06e87622399cb 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -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>, + _trait_ref: Option>, _vtable: Self::Value, ) { // TODO(antoyo) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index d8fbe51b975a0..c835ec11b4598 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -52,8 +52,7 @@ pub(crate) struct CodegenCx<'ll, 'tcx> { /// Cache instances of monomorphic and polymorphic items pub instances: RefCell, &'ll Value>>, /// Cache generated vtables - pub vtables: - RefCell, Option>), &'ll Value>>, + pub vtables: RefCell, Option>), &'ll Value>>, /// Cache of constant strings, pub const_str_cache: RefCell>, @@ -630,15 +629,14 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn vtables( &self, - ) -> &RefCell, Option>), &'ll Value>> - { + ) -> &RefCell, Option>), &'ll Value>> { &self.vtables } fn apply_vcall_visibility_metadata( &self, ty: Ty<'tcx>, - poly_trait_ref: Option>, + poly_trait_ref: Option>, vtable: &'ll Value, ) { apply_vcall_visibility_metadata(self, ty, poly_trait_ref, vtable); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 08d8f626c51ae..ca818cbbabf18 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -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}; @@ -1400,13 +1400,13 @@ 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>, + poly_trait_ref: Option>, ) -> &'ll DIType { let tcx = cx.tcx; let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref { let trait_ref = poly_trait_ref.with_self_ty(tcx, ty); - let trait_ref = tcx.erase_regions(tcx.instantiate_bound_regions_with_erased(trait_ref)); + let trait_ref = tcx.erase_regions(trait_ref); tcx.vtable_entries(trait_ref) } else { @@ -1491,7 +1491,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>( pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, - trait_ref: Option>, + trait_ref: Option>, vtable: &'ll Value, ) { // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in @@ -1510,7 +1510,7 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( 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(); @@ -1569,7 +1569,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>, + poly_trait_ref: Option>, vtable: &'ll Value, ) { if cx.dbg_cx.is_none() { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index a37e719d43f2b..af1d503ad6a43 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -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>, private::HiddenZst), + VTableTy(Ty<'tcx>, Option>, 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>, + implemented_trait: Option>, ) -> Self { assert_eq!( self_type, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 755f4816acf98..97964d830ffa5 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -585,7 +585,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn create_vtable_debuginfo( &self, ty: Ty<'tcx>, - trait_ref: Option>, + trait_ref: Option>, vtable: Self::Value, ) { metadata::create_vtable_di_node(self, ty, trait_ref, vtable) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 544578b29f107..be392b067c783 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -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}; @@ -128,14 +127,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 { @@ -210,7 +204,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), } } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 869798d8be194..d1b1ccbb8127d 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -507,7 +507,7 @@ pub enum VTableNameKind { pub fn compute_debuginfo_vtable_name<'tcx>( tcx: TyCtxt<'tcx>, t: Ty<'tcx>, - trait_ref: Option>, + trait_ref: Option>, 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); diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index c51a57be71cec..886951855401e 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -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,17 @@ 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> { +fn dyn_trait_in_self<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option> { for arg in ty.peel_refs().walk() { if let GenericArgKind::Type(ty) = arg.unpack() && let ty::Dynamic(data, _, _) = ty.kind() { - return data.principal(); + return data + .principal() + .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)); } } @@ -96,28 +101,24 @@ fn dyn_trait_in_self(ty: Ty<'_>) -> Option> { pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( cx: &Cx, ty: Ty<'tcx>, - poly_trait_ref: Option>, + trait_ref: Option>, ) -> Cx::Value { let tcx = cx.tcx(); // Check the cache. - if let Some(&val) = cx.vtables().borrow().get(&(ty, poly_trait_ref)) { + if let Some(&val) = cx.vtables().borrow().get(&(ty, trait_ref)) { return val; } - // FIXME(trait_upcasting): Take a non-higher-ranked vtable as arg. - let trait_ref = - poly_trait_ref.map(|trait_ref| tcx.instantiate_bound_regions_with_erased(trait_ref)); - let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref)); let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory(); let vtable_const = cx.const_data_from_alloc(vtable_allocation); let align = cx.data_layout().pointer_align.abi; let vtable = cx.static_addr_of(vtable_const, align, Some("vtable")); - cx.apply_vcall_visibility_metadata(ty, poly_trait_ref, vtable); - cx.create_vtable_debuginfo(ty, poly_trait_ref, vtable); - cx.vtables().borrow_mut().insert((ty, poly_trait_ref), vtable); + cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable); + cx.create_vtable_debuginfo(ty, trait_ref, vtable); + cx.vtables().borrow_mut().insert((ty, trait_ref), vtable); vtable } @@ -135,7 +136,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; diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index fe135e911fb3f..30d77c206a56f 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -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>, + trait_ref: Option>, vtable: Self::Value, ); diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 5b33fd7ab1028..4004947b46485 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -10,11 +10,11 @@ use super::BackendTypes; pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn vtables( &self, - ) -> &RefCell, Option>), Self::Value>>; + ) -> &RefCell, Option>), Self::Value>>; fn apply_vcall_visibility_metadata( &self, _ty: Ty<'tcx>, - _poly_trait_ref: Option>, + _poly_trait_ref: Option>, _vtable: Self::Value, ) { } diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 5c5ab435dbd79..4312c82815c16 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -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) } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 4ddf530a00d4a..45d89fbf92097 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -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) } From 9a7d1a4469124361f3b78f657f81ca9edfd799ec Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 11 Jan 2025 19:31:28 +0000 Subject: [PATCH 6/6] More assertions, tests, and miri coverage --- .../rustc_const_eval/src/interpret/call.rs | 2 +- .../rustc_const_eval/src/interpret/cast.rs | 54 +++++++++--------- .../src/traits/vtable.rs | 57 ++++++++----------- src/tools/miri/tests/pass/dyn-upcast.rs | 52 +++++++++++++++++ src/tools/miri/tests/pass/dyn-upcast.stdout | 2 + ...ltiple-supertraits-modulo-binder-vtable.rs | 2 + ...le-supertraits-modulo-binder-vtable.stderr | 4 +- .../multiple-supertraits-modulo-binder.rs | 2 + ...supertraits-modulo-normalization-vtable.rs | 3 +- ...rtraits-modulo-normalization-vtable.stderr | 4 +- ...ltiple-supertraits-modulo-normalization.rs | 2 + .../supertraits-modulo-inner-binder.rs | 30 ++++++++++ 12 files changed, 146 insertions(+), 68 deletions(-) create mode 100644 tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index e6a34193c9d69..ce39c40365509 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -693,7 +693,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(); diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 52bc2af928dce..e110c155da089 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -414,35 +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 = - 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); + 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 = + 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)?; diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index fb27051854e2d..780652c1cb104 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -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> { @@ -230,6 +228,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); @@ -307,6 +310,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!(); @@ -325,11 +333,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); } @@ -360,6 +366,12 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( ), ) -> Option { 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`. @@ -386,11 +398,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 { @@ -410,27 +420,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, diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs index 61410f7c4e0b5..f100c4d6a869e 100644 --- a/src/tools/miri/tests/pass/dyn-upcast.rs +++ b/src/tools/miri/tests/pass/dyn-upcast.rs @@ -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 . +fn modulo_binder() { + trait Supertrait { + fn _print_numbers(&self, mem: &[usize; 100]) { + println!("{mem:?}"); + } + } + impl Supertrait for () {} + + trait Trait: Supertrait + Supertrait { + fn say_hello(&self, _: &usize) { + println!("Hello!"); + } + } + impl Trait for () {} + + (&() as &'static dyn for<'a> Trait<&'static (), &'a ()> + as &'static dyn Trait<&'static (), &'static ()>) + .say_hello(&0); +} + +// Test for . +fn modulo_assoc() { + trait Supertrait { + fn _print_numbers(&self, mem: &[usize; 100]) { + println!("{mem:?}"); + } + } + impl Supertrait for () {} + + trait Identity { + type Selff; + } + impl Identity for Selff { + type Selff = Selff; + } + + trait Middle: Supertrait<()> + Supertrait { + fn say_hello(&self, _: &usize) { + println!("Hello!"); + } + } + impl Middle for () {} + + trait Trait: Middle<<() as Identity>::Selff> {} + impl Trait for () {} + + (&() as &dyn Trait as &dyn Middle<()>).say_hello(&0); +} diff --git a/src/tools/miri/tests/pass/dyn-upcast.stdout b/src/tools/miri/tests/pass/dyn-upcast.stdout index edd99a114a112..379600db3d91f 100644 --- a/src/tools/miri/tests/pass/dyn-upcast.stdout +++ b/src/tools/miri/tests/pass/dyn-upcast.stdout @@ -2,3 +2,5 @@ before goodbye before goodbye +Hello! +Hello! diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs index b41cf2e9f2679..796ddec46ac7a 100644 --- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs @@ -1,5 +1,7 @@ #![feature(rustc_attrs)] +// Test for . + trait Supertrait { fn _print_numbers(&self, mem: &[usize; 100]) { } diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr index 4bb7e1bdb6ad2..24fa1650ca14c 100644 --- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr @@ -5,7 +5,7 @@ error: vtable entries: [ Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)), Method( 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( as Supertrait<&()>>::_print_numbers - shim(reify)), Method( 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 ()>; | ^^^^^^^^^^^ diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs index 7bc069f4f4407..510a1471af293 100644 --- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs @@ -1,6 +1,8 @@ //@ run-pass //@ check-run-results +// Test for . + #![feature(trait_upcasting)] trait Supertrait { diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs index 22d6bf94d8706..69a71859a5cc7 100644 --- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs @@ -1,7 +1,8 @@ #![feature(rustc_attrs)] - #![feature(trait_upcasting)] +// Test for . + trait Supertrait { fn _print_numbers(&self, mem: &[usize; 100]) { println!("{mem:?}"); diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr index 04b1afae7beca..757e2dc69390c 100644 --- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr @@ -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( as Supertrait<()>>::_print_numbers - shim(reify)), Method( 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<()>; | ^^^^^^^^^^^^ diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs index fd0f62b4255a1..c744e6e64f575 100644 --- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs @@ -3,6 +3,8 @@ #![feature(trait_upcasting)] +// Test for . + trait Supertrait { fn _print_numbers(&self, mem: &[usize; 100]) { println!("{mem:?}"); diff --git a/tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs b/tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs new file mode 100644 index 0000000000000..6cd74b6c7f75f --- /dev/null +++ b/tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs @@ -0,0 +1,30 @@ +//@ run-pass + +#![feature(trait_upcasting)] + +trait Super { + fn call(&self) + where + U: HigherRanked, + { + } +} + +impl Super for () {} + +trait HigherRanked {} +impl HigherRanked for for<'a> fn(&'a ()) {} + +trait Unimplemented {} +impl HigherRanked for T {} + +trait Sub: Super + Super fn(&'a ())> {} +impl Sub for () {} + +fn main() { + let a: &dyn Sub = &(); + // `Super` and `Super fn(&'a ())>` have different + // vtables and we need to upcast to the latter! + let b: &dyn Super fn(&'a ())> = a; + b.call(); +}