Repository navigation
Add a !type annotation to a vtable for every supertrait that is a prefix of the vtable - #165475
folkertdev wants to merge 5 commits into
Conversation
25055da to
7663f53
Compare
This comment has been minimized.
This comment has been minimized.
7663f53 to
c36ddd6
Compare
This comment has been minimized.
This comment has been minimized.
3811141 to
c638b49
Compare
There was a problem hiding this comment.
On current nightly the drop does not run
|
r? WaffleLapkin |
|
|
|
Some changes occurred in compiler/rustc_codegen_llvm/src/debuginfo cc @Walnut356 |
This comment has been minimized.
This comment has been minimized.
In `with_self_ty` the def_id that is used is just `self.def_id`, so this is always the same DefId
c638b49 to
1490fdb
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
There was a problem hiding this comment.
Some minor comments;
More importantly, the type metadata / vfe seems very confusing. I wish we had some better docs for it... But I think I understood the problem? Here is how I understand it:
We want llvm to be able to de-virtualize calls through trait objects. The tool that LLVM provides for this is "type metadata" (& typed loads & vcall visibility). Type metadata is just a pair of (offset, ident) which we attach to the vtable constants, which says "'object' ident starts at offset in this constant". Then, when we load function pointers from vtables, we use llvm.type.checked.load with the ident of the vtable we are loading from. From that LLVM assumes that this load only takes pointers to constants maked with the same ident (+ the offset, but we always make it 0), and can figure out what are all the options for the loaded value.
The problem then comes when we use vtable of subtraits as the super trait's one, because it can hide an option for what the virtual function is, in cases where we don't ever generate the super trait's vtable separately. And so this can only happen with upcasting (and only the case where it uses a prefix of the vtable), because that's the only case where we load pointer from a vtable X but it's backed by a constant of vtable Y.
@folkertdev does that all sound correct?
| } | ||
|
|
||
| impl VCallVisibility { | ||
| fn for_def_id<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, def_id: DefId) -> Self { |
There was a problem hiding this comment.
IMO this should take a trait ref, not just the def id (as otherwise it's unclear def id of what should be passed).
| let Some(vcall_visibility) = trait_hierarchy | ||
| .iter() | ||
| .map(|trait_ref| VCallVisibility::for_def_id(cx, trait_ref.def_id)) | ||
| .min() | ||
| else { | ||
| return; | ||
| }; |
There was a problem hiding this comment.
Could you add a comment describing what a "minimal" visibility here means?
| let mut entries = vec![]; | ||
|
|
||
| let vtable_segment_callback = |segment| -> ControlFlow<()> { | ||
| if let VtblSegment::TraitOwnEntries { trait_ref, emit_vptr: false } = segment { |
There was a problem hiding this comment.
Could you add a comment explaining why we are checking for emit_vptr: false?
For now it gets at most one trait, the next step is to (recursively) provide supertraits too
1490fdb to
4573464
Compare
|
We discussed this in person, but just for posterity:
Yes. For Rust vtables the offset is always 0, C++ vtables actually use this offset parameter.
Yes, and it is important that we only ever use a checked load to load a function pointer from the vtable. A normal load is not allowed. We do use normal loads for the size and align fields of the vtable header. That is fine.
Yes, again this only works if we give LLVM accurate information. When we don't, it may miscompile.
Yes, specifically when we only perform checked loads for methods from the supertrait, and never with the subtrait.
Nit: generating the supertraits vtable separately by itself would not be sufficient. We'd also need to perform a type load for the specific functions that are used.
As discussed the case where the supertrait's vtable is not a prefix is also broken. But yeah the idea is just that before this PR the vtable for Trait could be removed (correctly, based on the information that we provided to LLVM) even though we might actually be using methods from the supertrait(s). @bors r=WaffleLapkin |
Add a `!type` annotation to a vtable for every supertrait that is a prefix of the vtable tracking issue: rust-lang#68262 This fixes a miscompilation where a vtable was incorrectly eliminated because it was only used by a supertrait. In https://godbolt.org/z/8TE1WMvac, `call_super` could be applied to both `A` and `B`, but it incorrectly just always calls the `B` method. The vtable for A has been eliminated. The fix here only works for the first supertrait in the list, or later ones if they have no methods. So in `trait Foo: A + B`, there is still a bug when B actually has methods (and its vtable is not a prefix of `Foo`s vtable). In that case the vtable for `B` does not get any `!type` annotations, which provides incomplete information to VFE and it may therefore miscompile. That needs some further refactoring, which is blocked on rust-lang#164808. Best reviewed commit-by-commit.
Rollup of 17 pull requests Successful merges: - #160618 (require a consistent codegen backend when -Zllvm-target-feature is present) - #164043 (alloc: Unstably add `Vec::repeat` and `Vec::repeat_in`) - #164808 (asm const ptr: move name generation to asm-specific logic) - #164823 (Use `DerefMut` and `IndexMut` before `&raw mut` autoborrow) - #165475 (Add a `!type` annotation to a vtable for every supertrait that is a prefix of the vtable) - #165568 (rustdoc: Suggest corrections for reference label typos) - #165924 (rustc_codegen_gcc subtree update 2026-11-13) - #165927 (Remove `const_struct` from `rustc_codegen_ssa`) - #164362 (Emit note for `non_exhaustive` within defining crate for `unreachable_patterns`) - #164997 (Fix as str suggestion in subpattern) - #165825 (make `Complex<x87_f80>` ABI-compatible with C) - #165868 (Small changes to dyn compatibility) - #165879 (Add `#[rustc_test_entrypoint_marker]` back !) - #165903 (Fix borrowck ICE when a borrow outlives a local in a const block) - #165914 (Use `trim_prefix` in bootstrap) - #165919 (tests: accept better behavior on underaligned accesses) - #165936 (Enhance documentation for try_clone File implementation)
Add a `!type` annotation to a vtable for every supertrait that is a prefix of the vtable tracking issue: rust-lang#68262 This fixes a miscompilation where a vtable was incorrectly eliminated because it was only used by a supertrait. In https://godbolt.org/z/8TE1WMvac, `call_super` could be applied to both `A` and `B`, but it incorrectly just always calls the `B` method. The vtable for A has been eliminated. The fix here only works for the first supertrait in the list, or later ones if they have no methods. So in `trait Foo: A + B`, there is still a bug when B actually has methods (and its vtable is not a prefix of `Foo`s vtable). In that case the vtable for `B` does not get any `!type` annotations, which provides incomplete information to VFE and it may therefore miscompile. That needs some further refactoring, which is blocked on rust-lang#164808. Best reviewed commit-by-commit.
Rollup of 16 pull requests Successful merges: - #160618 (require a consistent codegen backend when -Zllvm-target-feature is present) - #164043 (alloc: Unstably add `Vec::repeat` and `Vec::repeat_in`) - #164823 (Use `DerefMut` and `IndexMut` before `&raw mut` autoborrow) - #165475 (Add a `!type` annotation to a vtable for every supertrait that is a prefix of the vtable) - #165568 (rustdoc: Suggest corrections for reference label typos) - #165924 (rustc_codegen_gcc subtree update 2026-11-13) - #165927 (Remove `const_struct` from `rustc_codegen_ssa`) - #164362 (Emit note for `non_exhaustive` within defining crate for `unreachable_patterns`) - #164997 (Fix as str suggestion in subpattern) - #165825 (make `Complex<x87_f80>` ABI-compatible with C) - #165868 (Small changes to dyn compatibility) - #165879 (Add `#[rustc_test_entrypoint_marker]` back !) - #165903 (Fix borrowck ICE when a borrow outlives a local in a const block) - #165914 (Use `trim_prefix` in bootstrap) - #165919 (tests: accept better behavior on underaligned accesses) - #165936 (Enhance documentation for try_clone File implementation)
tracking issue: #68262
This fixes a miscompilation where a vtable was incorrectly eliminated because it was only used by a supertrait.
In https://godbolt.org/z/8TE1WMvac,
call_supercould be applied to bothAandB, but it incorrectly just always calls theBmethod. The vtable for A has been eliminated.The fix here only works for the first supertrait in the list, or later ones if they have no methods. So in
trait Foo: A + B, there is still a bug when B actually has methods (and its vtable is not a prefix ofFoos vtable).In that case the vtable for
Bdoes not get any!typeannotations, which provides incomplete information to VFE and it may therefore miscompile. That needs some further refactoring, which is blocked on #164808.Best reviewed commit-by-commit.