Skip to content

Commit 35d55b3

Browse files
committed
Auto merge of #145807 - zachs18:only-consider-auto-traits-empty, r=compiler-errors
When determining if a trait has no entries for the purposes of omitting vptrs from subtrait vtables, consider its transitive supertraits' entries, instead of just its own entries. When determining if a non-first supertrait vptr can be omitted from a subtrait vtable, check if the supertrait or any of its (transitive) supertraits have methods, instead of only checking if the supertrait itself has methods. This fixes the soundness issue where a vptr would be omitted for a supertrait with no methods but that itself had a supertrait with methods, while still optimizing the case where the supertrait is "truly" empty (it has no own vtable entries, and none of its (transitive) supertraits have any own vtable entries). Fixes <#145752> ----- Old description: ~~Treat all non-auto traits as non-empty (possibly having methods) for purposes of determining if we need to emit a vptr for a non-direct supertrait (and for new "sibling" entries after a direct or non-direct supertrait).~~ This fixes (I believe) the soundness issue, ~~but regresses vtable sizes and possibly upcasting perf in some cases when using trait hierarchies with empty non-auto traits (see `tests/ui/traits/vtable/multiple-markers.stderr`) since we use vptrs in some cases where we could re-use the vtable.~~ Fixes <#145752> Re-opens (not anymore) <#114942> Should not affect <#131813> (i.e. the soundness issue is still fixed, ~~though the relevant vtables in the `trait Evil` example will be larger now~~) cc implementation history <#131864> <#113856> ----- ~~It should be possible to check if a trait has any methods from itself *or* supertraits (instead of just from itself), but to fix the immediate soundness issue, just assume any non-auto trait could have methods. A more optimistic check can be implemented later (or if someone does it soon it could just supercede this PR 😄).~~ Done in latest push `@rustbot` label A-dyn-trait F-trait_upcasting
2 parents 1f7dcc8 + 904e83c commit 35d55b3

File tree

5 files changed

+167
-1
lines changed

5 files changed

+167
-1
lines changed

compiler/rustc_trait_selection/src/traits/vtable.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,12 @@ fn prepare_vtable_segments_inner<'tcx, T>(
153153

154154
// emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
155155
while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() {
156-
let has_entries = has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id);
156+
// We don't need to emit a vptr for "truly-empty" supertraits, but we *do* need to emit a
157+
// vptr for supertraits that have no methods, but that themselves have supertraits
158+
// with methods, so we check if any transitive supertrait has entries here (this includes
159+
// the trait itself).
160+
let has_entries = ty::elaborate::supertrait_def_ids(tcx, inner_most_trait_ref.def_id)
161+
.any(|def_id| has_own_existential_vtable_entries(tcx, def_id));
157162

158163
segment_visitor(VtblSegment::TraitOwnEntries {
159164
trait_ref: inner_most_trait_ref,
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: vtable entries: [
2+
MetadataDropInPlace,
3+
MetadataSize,
4+
MetadataAlign,
5+
Method(<dyn OneTwo as One>::one - shim(reify)),
6+
Method(<dyn OneTwo as Two>::two - shim(reify)),
7+
TraitVPtr(<dyn OneTwo as Two>),
8+
TraitVPtr(<dyn OneTwo as TwoAgain>),
9+
]
10+
--> $DIR/empty-supertrait-with-nonempty-supersupertrait.rs:40:1
11+
|
12+
LL | type T = dyn OneTwo;
13+
| ^^^^^^
14+
15+
error: aborting due to 1 previous error
16+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//@ revisions: run dump
2+
//@[run] run-pass
3+
//@[dump] check-fail
4+
//! Regression test for #145752
5+
//! Ensure that `OneTwo` contains a vptr for `TwoAgain`
6+
#![allow(unused)]
7+
#![cfg_attr(dump, feature(rustc_attrs))]
8+
9+
trait One {
10+
fn one(&self) {
11+
panic!("don't call this");
12+
}
13+
}
14+
impl One for () {}
15+
16+
trait Two {
17+
fn two(&self) {
18+
println!("good");
19+
}
20+
}
21+
impl Two for () {}
22+
23+
trait TwoAgain: Two {}
24+
impl<T: Two> TwoAgain for T {}
25+
26+
trait OneTwo: One + TwoAgain {}
27+
impl<T: One + Two> OneTwo for T {}
28+
29+
fn main() {
30+
(&()).two();
31+
(&() as &dyn OneTwo).two();
32+
(&() as &dyn OneTwo as &dyn Two).two();
33+
34+
// these two used to panic because they called `one` due to #145752
35+
(&() as &dyn OneTwo as &dyn TwoAgain).two();
36+
(&() as &dyn OneTwo as &dyn TwoAgain as &dyn Two).two();
37+
}
38+
39+
#[cfg_attr(dump, rustc_dump_vtable)]
40+
type T = dyn OneTwo;
41+
//[dump]~^ ERROR vtable entries: [
42+
//[dump]~| ERROR MetadataDropInPlace,
43+
//[dump]~| ERROR MetadataSize,
44+
//[dump]~| ERROR MetadataAlign,
45+
//[dump]~| ERROR Method(<dyn OneTwo as One>::one - shim(reify)),
46+
//[dump]~| ERROR Method(<dyn OneTwo as Two>::two - shim(reify)),
47+
//[dump]~| ERROR TraitVPtr(<dyn OneTwo as Two>),
48+
//[dump]~| ERROR TraitVPtr(<dyn OneTwo as TwoAgain>),
49+
//[dump]~| ERROR ]
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Related to <https://github.com/rust-lang/rust/issues/113840>
2+
//
3+
// This test makes sure that multiple auto traits can reuse the
4+
// same pointer for upcasting (e.g. `Send`/`Sync`)
5+
6+
#![crate_type = "lib"]
7+
#![feature(rustc_attrs, auto_traits)]
8+
9+
// Markers
10+
auto trait M0 {}
11+
auto trait M1 {}
12+
auto trait M2 {}
13+
14+
// Just a trait with a method
15+
trait T {
16+
fn method(&self) {}
17+
}
18+
19+
trait A: M0 + M1 + M2 + T {}
20+
21+
trait B: M0 + M1 + T + M2 {}
22+
23+
trait C: M0 + T + M1 + M2 {}
24+
25+
trait D: T + M0 + M1 + M2 {}
26+
27+
struct S;
28+
29+
impl M0 for S {}
30+
impl M1 for S {}
31+
impl M2 for S {}
32+
impl T for S {}
33+
34+
#[rustc_dump_vtable]
35+
impl A for S {}
36+
//~^ ERROR vtable entries
37+
38+
#[rustc_dump_vtable]
39+
impl B for S {}
40+
//~^ ERROR vtable entries
41+
42+
#[rustc_dump_vtable]
43+
impl C for S {}
44+
//~^ ERROR vtable entries
45+
46+
#[rustc_dump_vtable]
47+
impl D for S {}
48+
//~^ ERROR vtable entries
49+
50+
fn main() {}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
error: vtable entries: [
2+
MetadataDropInPlace,
3+
MetadataSize,
4+
MetadataAlign,
5+
Method(<S as T>::method),
6+
]
7+
--> $DIR/multiple-auto.rs:35:1
8+
|
9+
LL | impl A for S {}
10+
| ^^^^^^^^^^^^
11+
12+
error: vtable entries: [
13+
MetadataDropInPlace,
14+
MetadataSize,
15+
MetadataAlign,
16+
Method(<S as T>::method),
17+
]
18+
--> $DIR/multiple-auto.rs:39:1
19+
|
20+
LL | impl B for S {}
21+
| ^^^^^^^^^^^^
22+
23+
error: vtable entries: [
24+
MetadataDropInPlace,
25+
MetadataSize,
26+
MetadataAlign,
27+
Method(<S as T>::method),
28+
]
29+
--> $DIR/multiple-auto.rs:43:1
30+
|
31+
LL | impl C for S {}
32+
| ^^^^^^^^^^^^
33+
34+
error: vtable entries: [
35+
MetadataDropInPlace,
36+
MetadataSize,
37+
MetadataAlign,
38+
Method(<S as T>::method),
39+
]
40+
--> $DIR/multiple-auto.rs:47:1
41+
|
42+
LL | impl D for S {}
43+
| ^^^^^^^^^^^^
44+
45+
error: aborting due to 4 previous errors
46+

0 commit comments

Comments
 (0)