Skip to content

Commit b7cc991

Browse files
Turn tcx.vtable_allocation() into a query.
1 parent a1e2c0f commit b7cc991

File tree

9 files changed

+98
-69
lines changed

9 files changed

+98
-69
lines changed

compiler/rustc_codegen_cranelift/src/vtable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub(crate) fn get_vtable<'tcx>(
6868
ty: Ty<'tcx>,
6969
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
7070
) -> Value {
71-
let alloc_id = fx.tcx.vtable_allocation(ty, trait_ref);
71+
let alloc_id = fx.tcx.vtable_allocation((ty, trait_ref));
7272
let data_id =
7373
data_id_for_alloc_id(&mut fx.constants_cx, &mut *fx.module, alloc_id, Mutability::Not);
7474
let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);

compiler/rustc_codegen_ssa/src/meth.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
7272
return val;
7373
}
7474

75-
let vtable_alloc_id = tcx.vtable_allocation(ty, trait_ref);
75+
let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref));
7676
let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory();
7777
let vtable_const = cx.const_data_from_alloc(vtable_allocation);
7878
let align = cx.data_layout().pointer_align.abi;

compiler/rustc_const_eval/src/interpret/traits.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
3030
ensure_monomorphic_enough(*self.tcx, ty)?;
3131
ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;
3232

33-
let vtable_allocation = self.tcx.vtable_allocation(ty, poly_trait_ref);
33+
let vtable_allocation = self.tcx.vtable_allocation((ty, poly_trait_ref));
3434

3535
let vtable_ptr = self.memory.global_base_pointer(Pointer::from(vtable_allocation))?;
3636

compiler/rustc_middle/src/query/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,13 @@ rustc_queries! {
10121012
key.1, key.0 }
10131013
}
10141014

1015+
query vtable_allocation(key: (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>)) -> mir::interpret::AllocId {
1016+
desc { |tcx| "vtable const allocation for <{} as {}>",
1017+
key.0,
1018+
key.1.map(|trait_ref| format!("{}", trait_ref)).unwrap_or("_".to_owned())
1019+
}
1020+
}
1021+
10151022
query codegen_fulfill_obligation(
10161023
key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
10171024
) -> Result<ImplSource<'tcx, ()>, ErrorReported> {

compiler/rustc_middle/src/ty/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2050,6 +2050,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
20502050
trait_impls_of: trait_def::trait_impls_of_provider,
20512051
type_uninhabited_from: inhabitedness::type_uninhabited_from,
20522052
const_param_default: consts::const_param_default,
2053+
vtable_allocation: vtable::vtable_allocation_provider,
20532054
..*providers
20542055
};
20552056
}

compiler/rustc_middle/src/ty/print/pretty.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2192,6 +2192,7 @@ forward_display_to_print! {
21922192
// because `for<'tcx>` isn't possible yet.
21932193
ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>,
21942194
ty::Binder<'tcx, ty::TraitRef<'tcx>>,
2195+
ty::Binder<'tcx, ty::ExistentialTraitRef<'tcx>>,
21952196
ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
21962197
ty::Binder<'tcx, ty::FnSig<'tcx>>,
21972198
ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,

compiler/rustc_middle/src/ty/vtable.rs

+60-65
Original file line numberDiff line numberDiff line change
@@ -43,77 +43,72 @@ pub const COMMON_VTABLE_ENTRIES_DROPINPLACE: usize = 0;
4343
pub const COMMON_VTABLE_ENTRIES_SIZE: usize = 1;
4444
pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2;
4545

46-
impl<'tcx> TyCtxt<'tcx> {
47-
/// Retrieves an allocation that represents the contents of a vtable.
48-
/// There's a cache within `TyCtxt` so it will be deduplicated.
49-
pub fn vtable_allocation(
50-
self,
51-
ty: Ty<'tcx>,
52-
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
53-
) -> AllocId {
54-
let tcx = self;
46+
/// Retrieves an allocation that represents the contents of a vtable.
47+
/// Since this is a query, allocations are cached and not duplicated.
48+
pub(super) fn vtable_allocation_provider<'tcx>(
49+
tcx: TyCtxt<'tcx>,
50+
key: (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>),
51+
) -> AllocId {
52+
let (ty, poly_trait_ref) = key;
5553

56-
let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref {
57-
let trait_ref = poly_trait_ref.with_self_ty(tcx, ty);
58-
let trait_ref = tcx.erase_regions(trait_ref);
54+
let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref {
55+
let trait_ref = poly_trait_ref.with_self_ty(tcx, ty);
56+
let trait_ref = tcx.erase_regions(trait_ref);
5957

60-
tcx.vtable_entries(trait_ref)
61-
} else {
62-
COMMON_VTABLE_ENTRIES
63-
};
64-
65-
let layout = tcx
66-
.layout_of(ty::ParamEnv::reveal_all().and(ty))
67-
.expect("failed to build vtable representation");
68-
assert!(!layout.is_unsized(), "can't create a vtable for an unsized type");
69-
let size = layout.size.bytes();
70-
let align = layout.align.abi.bytes();
58+
tcx.vtable_entries(trait_ref)
59+
} else {
60+
COMMON_VTABLE_ENTRIES
61+
};
7162

72-
let ptr_size = tcx.data_layout.pointer_size;
73-
let ptr_align = tcx.data_layout.pointer_align.abi;
63+
let layout = tcx
64+
.layout_of(ty::ParamEnv::reveal_all().and(ty))
65+
.expect("failed to build vtable representation");
66+
assert!(!layout.is_unsized(), "can't create a vtable for an unsized type");
67+
let size = layout.size.bytes();
68+
let align = layout.align.abi.bytes();
7469

75-
let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap();
76-
let mut vtable =
77-
Allocation::uninit(vtable_size, ptr_align, /* panic_on_fail */ true).unwrap();
70+
let ptr_size = tcx.data_layout.pointer_size;
71+
let ptr_align = tcx.data_layout.pointer_align.abi;
7872

79-
// No need to do any alignment checks on the memory accesses below, because we know the
80-
// allocation is correctly aligned as we created it above. Also we're only offsetting by
81-
// multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
73+
let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap();
74+
let mut vtable = Allocation::uninit(vtable_size, ptr_align, /* panic_on_fail */ true).unwrap();
8275

83-
for (idx, entry) in vtable_entries.iter().enumerate() {
84-
let idx: u64 = u64::try_from(idx).unwrap();
85-
let scalar = match entry {
86-
VtblEntry::MetadataDropInPlace => {
87-
let instance = ty::Instance::resolve_drop_in_place(tcx, ty);
88-
let fn_alloc_id = tcx.create_fn_alloc(instance);
89-
let fn_ptr = Pointer::from(fn_alloc_id);
90-
ScalarMaybeUninit::from_pointer(fn_ptr, &tcx)
91-
}
92-
VtblEntry::MetadataSize => Scalar::from_uint(size, ptr_size).into(),
93-
VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size).into(),
94-
VtblEntry::Vacant => continue,
95-
VtblEntry::Method(instance) => {
96-
// Prepare the fn ptr we write into the vtable.
97-
let instance = instance.polymorphize(tcx);
98-
let fn_alloc_id = tcx.create_fn_alloc(instance);
99-
let fn_ptr = Pointer::from(fn_alloc_id);
100-
ScalarMaybeUninit::from_pointer(fn_ptr, &tcx)
101-
}
102-
VtblEntry::TraitVPtr(trait_ref) => {
103-
let super_trait_ref = trait_ref.map_bound(|trait_ref| {
104-
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
105-
});
106-
let supertrait_alloc_id = self.vtable_allocation(ty, Some(super_trait_ref));
107-
let vptr = Pointer::from(supertrait_alloc_id);
108-
ScalarMaybeUninit::from_pointer(vptr, &tcx)
109-
}
110-
};
111-
vtable
112-
.write_scalar(&tcx, alloc_range(ptr_size * idx, ptr_size), scalar)
113-
.expect("failed to build vtable representation");
114-
}
76+
// No need to do any alignment checks on the memory accesses below, because we know the
77+
// allocation is correctly aligned as we created it above. Also we're only offsetting by
78+
// multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
11579

116-
vtable.mutability = Mutability::Not;
117-
tcx.create_memory_alloc(tcx.intern_const_alloc(vtable))
80+
for (idx, entry) in vtable_entries.iter().enumerate() {
81+
let idx: u64 = u64::try_from(idx).unwrap();
82+
let scalar = match entry {
83+
VtblEntry::MetadataDropInPlace => {
84+
let instance = ty::Instance::resolve_drop_in_place(tcx, ty);
85+
let fn_alloc_id = tcx.create_fn_alloc(instance);
86+
let fn_ptr = Pointer::from(fn_alloc_id);
87+
ScalarMaybeUninit::from_pointer(fn_ptr, &tcx)
88+
}
89+
VtblEntry::MetadataSize => Scalar::from_uint(size, ptr_size).into(),
90+
VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size).into(),
91+
VtblEntry::Vacant => continue,
92+
VtblEntry::Method(instance) => {
93+
// Prepare the fn ptr we write into the vtable.
94+
let instance = instance.polymorphize(tcx);
95+
let fn_alloc_id = tcx.create_fn_alloc(instance);
96+
let fn_ptr = Pointer::from(fn_alloc_id);
97+
ScalarMaybeUninit::from_pointer(fn_ptr, &tcx)
98+
}
99+
VtblEntry::TraitVPtr(trait_ref) => {
100+
let super_trait_ref = trait_ref
101+
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
102+
let supertrait_alloc_id = tcx.vtable_allocation((ty, Some(super_trait_ref)));
103+
let vptr = Pointer::from(supertrait_alloc_id);
104+
ScalarMaybeUninit::from_pointer(vptr, &tcx)
105+
}
106+
};
107+
vtable
108+
.write_scalar(&tcx, alloc_range(ptr_size * idx, ptr_size), scalar)
109+
.expect("failed to build vtable representation");
118110
}
111+
112+
vtable.mutability = Mutability::Not;
113+
tcx.create_memory_alloc(tcx.intern_const_alloc(vtable))
119114
}

compiler/rustc_query_impl/src/keys.rs

+11
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ impl<'tcx> Key for mir::interpret::GlobalId<'tcx> {
7272
}
7373
}
7474

75+
impl<'tcx> Key for (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>) {
76+
#[inline(always)]
77+
fn query_crate_is_local(&self) -> bool {
78+
true
79+
}
80+
81+
fn default_span(&self, _: TyCtxt<'_>) -> Span {
82+
DUMMY_SP
83+
}
84+
}
85+
7586
impl<'tcx> Key for mir::interpret::LitToConstInput<'tcx> {
7687
#[inline(always)]
7788
fn query_crate_is_local(&self) -> bool {

src/test/incremental/reorder_vtable.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
// revisions:rpass1 rpass2
22

3+
// This test case makes sure re-order the methods in a vtable will
4+
// trigger recompilation of codegen units that instantiate it.
5+
//
6+
// See https://github.com/rust-lang/rust/issues/89598
7+
38
trait Foo {
49
#[cfg(rpass1)]
510
fn method1(&self) -> u32;
@@ -16,12 +21,21 @@ impl Foo for u32 {
1621
}
1722

1823
fn main() {
24+
// Before #89598 was fixed, the vtable allocation would be cached during
25+
// a MIR optimization pass and then the codegen pass for the main object
26+
// file would not register a dependency on it (because of the missing
27+
// dep-tracking).
28+
//
29+
// In the rpass2 session, the main object file would not be re-compiled,
30+
// thus the mod1::foo(x) call would pass in an outdated vtable, while the
31+
// mod1 object would expect the new, re-ordered vtable, resulting in a
32+
// call to the wrong method.
1933
let x: &dyn Foo = &0u32;
2034
assert_eq!(mod1::foo(x), 17);
2135
}
2236

2337
mod mod1 {
24-
pub fn foo(x: &dyn super::Foo) -> u32 {
38+
pub(super) fn foo(x: &dyn super::Foo) -> u32 {
2539
x.method1()
2640
}
2741
}

0 commit comments

Comments
 (0)