rust/compiler/rustc_codegen_cranelift/src
bors c37fbd873a Auto merge of #135318 - compiler-errors:vtable-fixes, r=lcnr
Fix deduplication mismatches in vtables leading to upcasting unsoundness

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

### #135315

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

```
trait Supertrait<T> {
    fn _print_numbers(&self, mem: &[usize; 100]) {
        println!("{mem:?}");
    }
}
impl<T> Supertrait<T> for () {}

trait Identity {
    type Selff;
}
impl<Selff> Identity for Selff {
    type Selff = Selff;
}

trait Middle<T>: Supertrait<()> + Supertrait<T> {
    fn say_hello(&self, _: &usize) {
        println!("Hello!");
    }
}
impl<T> Middle<T> for () {}

trait Trait: Middle<<() as Identity>::Selff> {}
impl Trait for () {}

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

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

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

### #135316

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

```
trait Supertrait<T> {
    fn _print_numbers(&self, mem: &[usize; 100]) {
        println!("{mem:?}");
    }
}
impl<T> Supertrait<T> for () {}

trait Trait<T, U>: Supertrait<T> + Supertrait<U> {
    fn say_hello(&self, _: &usize) {
        println!("Hello!");
    }
}
impl<T, U> Trait<T, U> for () {}

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

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

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

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

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

----

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

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

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

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

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

Fixes #135315
Fixes #135316
2025-01-31 04:09:11 +00:00
..
abi Auto merge of #134290 - tgross35:windows-i128-callconv, r=bjorn3,wesleywiser 2025-01-28 06:11:13 +00:00
debuginfo remove remaining references to Reveal 2024-11-23 13:52:56 +01:00
driver Change collect_and_partition_mono_items tuple return type to a struct 2025-01-27 09:38:12 +00:00
intrinsics introduce ty::Value 2025-01-30 17:47:44 +01:00
optimize Reformat use declarations. 2024-07-29 08:26:52 +10:00
allocator.rs Reformat using the new identifier sorting from rustfmt 2024-09-22 19:11:29 -04:00
analyze.rs rename AddressOf -> RawBorrow inside the compiler 2024-08-18 19:46:53 +02:00
base.rs Revert "Auto merge of #134330 - scottmcm:no-more-rvalue-len, r=matthewjasper" 2025-01-18 22:09:34 +00:00
cast.rs Windows x86: Change i128 to return via the vector ABI 2025-01-27 12:12:59 +00:00
codegen_i128.rs Windows x86: Change i128 to return via the vector ABI 2025-01-27 12:12:59 +00:00
common.rs remove remaining references to Reveal 2024-11-23 13:52:56 +01:00
compiler_builtins.rs Use a C-safe return type for __rust_[ui]128_* overflowing intrinsics 2025-01-15 03:49:39 +00:00
concurrency_limiter.rs Remove jobserver from Session 2024-12-13 10:21:22 +00:00
config.rs Merge commit '57845a397e' into sync_cg_clif-2024-12-06 2024-12-06 12:10:30 +00:00
constant.rs Use ExistentialTraitRef throughout codegen 2025-01-30 15:34:00 +00:00
discriminant.rs make no-variant types a dedicated Variants variant 2024-12-18 11:01:54 +01:00
global_asm.rs remove remaining references to Reveal 2024-11-23 13:52:56 +01:00
inline_asm.rs Merge commit '918acafef6' into sync_cg_clif-2025-01-05 2025-01-05 15:44:46 +00:00
lib.rs Merge commit '918acafef6' into sync_cg_clif-2025-01-05 2025-01-05 15:44:46 +00:00
linkage.rs Sync rustc_codegen_cranelift 'ddd4ce2553' 2021-04-30 14:49:58 +02:00
main_shim.rs remove support for the #[start] attribute 2025-01-21 06:59:15 -07:00
num.rs Merge commit 'e39eacd2d4' into sync_cg_clif-2025-01-10 2025-01-10 09:02:07 +00:00
pointer.rs Merge commit '1fa693ca44' into sync_cg_clif-2024-11-09 2024-11-09 13:48:06 +00:00
pretty_clif.rs Merge commit '1fa693ca44' into sync_cg_clif-2024-11-09 2024-11-09 13:48:06 +00:00
toolchain.rs Merge commit 'e9d1a0a7b0' into sync_cg_clif-2022-08-24 2022-08-24 18:40:58 +02:00
trap.rs Merge commit '5b1246bb4b' into sync_cg_clif-2024-11-02 2024-11-02 14:53:30 +00:00
unsize.rs Use ExistentialTraitRef throughout codegen 2025-01-30 15:34:00 +00:00
unwind_module.rs Merge commit '49cd5dd454' into sync_cg_clif-2024-06-30 2024-06-30 11:28:14 +00:00
value_and_place.rs Remove polymorphization 2024-12-06 16:42:09 -05:00
vtable.rs Use ExistentialTraitRef throughout codegen 2025-01-30 15:34:00 +00:00