Skip to content

Commit 7c67713

Browse files
author
Alexander Regueiro
committed
Implemented trait upcasting.
1 parent 1357c9b commit 7c67713

File tree

8 files changed

+176
-91
lines changed

8 files changed

+176
-91
lines changed

src/librustc_codegen_ssa/base.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::back::write::{
2020
use crate::common::{IntPredicate, RealPredicate, TypeKind};
2121
use crate::meth;
2222
use crate::mir;
23-
use crate::mir::operand::OperandValue;
23+
use crate::mir::operand::{OperandRef, OperandValue};
2424
use crate::mir::place::PlaceRef;
2525
use crate::traits::*;
2626
use crate::{CachedModuleCodegen, CrateInfo, MemFlags, ModuleCodegen, ModuleKind};
@@ -221,6 +221,57 @@ pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
221221
}
222222
}
223223

224+
/// Coerces `op` to `dst.ty`. `op` may be a fat pointer.
225+
pub fn coerce_ptr_unsized<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
226+
bx: &mut Bx,
227+
op: OperandRef<'tcx, Bx::Value>,
228+
dst: TyLayout<'tcx>,
229+
) -> OperandValue<Bx::Value> {
230+
assert!(bx.cx().is_backend_scalar_pair(dst));
231+
let src = op.layout;
232+
let (base, info) = match op.val {
233+
OperandValue::Pair(base, info) => {
234+
// Fat-ptr to fat-ptr unsize (e.g., `&'a fmt::Debug + Send` => `&'a fmt::Debug`).
235+
// We always need to `pointercast` the base to ensure the types match up.
236+
// In some cases, e.g., `&'a Bar` => `&'a Foo` where `Bar: Foo`, we also need to
237+
// modify the info (i.e., the pointer to the vtable), by offsetting into the
238+
// existing vtable. See the `get_vtable` fn in `meth.rs` for more details of vtable
239+
// layout.
240+
241+
let base = bx.pointercast(
242+
base,
243+
bx.cx().scalar_pair_element_backend_type(dst, 0, true),
244+
);
245+
let info = match (&src.ty.kind, &dst.ty.kind) {
246+
(&ty::Ref(_, a, _),
247+
&ty::Ref(_, b, _)) |
248+
(&ty::Ref(_, a, _),
249+
&ty::RawPtr(ty::TypeAndMut { ty: b, .. })) |
250+
(&ty::RawPtr(ty::TypeAndMut { ty: a, .. }),
251+
&ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
252+
unsized_info(bx, a, b, Some(info))
253+
}
254+
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
255+
assert_eq!(def_a, def_b);
256+
unsized_info(bx, src.ty, dst.ty, Some(info))
257+
}
258+
_ => bug!("coerce_ptr_unsized: called on bad types"),
259+
};
260+
(base, info)
261+
}
262+
OperandValue::Immediate(base) => {
263+
unsize_thin_ptr(bx, base, src.ty, dst.ty)
264+
}
265+
OperandValue::Ref(..) => {
266+
bug!(
267+
"coerce_ptr_unsized: unexpected by-ref operand {:?}",
268+
op
269+
);
270+
}
271+
};
272+
OperandValue::Pair(base, info)
273+
}
274+
224275
/// Coerces `src`, which is a reference to a value of type `src_ty`,
225276
/// to a value of type `dst_ty`, and stores the result in `dst`.
226277
pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(

src/librustc_codegen_ssa/meth.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,21 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
8585
(&[]).iter()
8686
};
8787

88-
let methods = methods.cloned().map(|opt_mth| {
89-
opt_mth.map_or(nullptr, |(def_id, substs)| {
90-
cx.get_fn_addr(
91-
ty::Instance::resolve_for_vtable(
92-
cx.tcx(),
93-
ty::ParamEnv::reveal_all(),
94-
def_id,
95-
substs,
88+
// Resolve instances for each method in the vtable.
89+
// Preserve nested (per-supertrait) structure of list of methods.
90+
let methods = methods.cloned().map(|trait_methods| {
91+
trait_methods.into_iter().map(|opt_meth| {
92+
opt_meth.map_or(nullptr, |(def_id, substs)| {
93+
cx.get_fn_addr(
94+
ty::Instance::resolve_for_vtable(
95+
cx.tcx(),
96+
ty::ParamEnv::reveal_all(),
97+
def_id,
98+
substs,
99+
)
100+
.unwrap(),
96101
)
97-
.unwrap(),
98-
)
102+
})
99103
})
100104
});
101105

@@ -104,15 +108,20 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
104108
// If you touch this code, be sure to also make the corresponding changes to
105109
// `get_vtable` in `rust_mir/interpret/traits.rs`.
106110
// /////////////////////////////////////////////////////////////////////////////////////////////
107-
let components: Vec<_> = [
111+
let metadata = [
108112
cx.get_fn_addr(Instance::resolve_drop_in_place(cx.tcx(), ty)),
109113
cx.const_usize(layout.size.bytes()),
110114
cx.const_usize(layout.align.abi.bytes()),
111-
]
112-
.iter()
113-
.cloned()
114-
.chain(methods)
115-
.collect();
115+
];
116+
let mut components: Vec<_> = methods
117+
.flat_map(|trait_methods| {
118+
// Insert copy of metadata before methods for the current (super)trait.
119+
metadata.iter().cloned().chain(trait_methods)
120+
})
121+
.collect();
122+
if components.is_empty() {
123+
components.extend(&metadata);
124+
}
116125

117126
let vtable_const = cx.const_struct(&components, false);
118127
let align = cx.data_layout().pointer_align.abi;

src/librustc_middle/query/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,9 @@ rustc_queries! {
670670
}
671671

672672
Other {
673-
query vtable_methods(key: ty::PolyTraitRef<'tcx>)
674-
-> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] {
673+
query vtable_methods(
674+
key: ty::PolyTraitRef<'tcx>
675+
) -> &'tcx [&'tcx [Option<(DefId, SubstsRef<'tcx>)>]] {
675676
desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) }
676677
}
677678
}

src/librustc_middle/traits/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,6 @@ pub enum MethodViolationCode {
834834
/// e.g., `fn foo<A>()`
835835
Generic,
836836

837-
/// the method's receiver (`self` argument) can't be dispatched on
837+
/// The method's receiver (`self` argument) cannot be dispatched on.
838838
UndispatchableReceiver,
839839
}

src/librustc_mir/interpret/traits.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
5656
// `get_vtable` in `rust_codegen_llvm/meth.rs`.
5757
// /////////////////////////////////////////////////////////////////////////////////////////
5858
let vtable = self.memory.allocate(
59-
ptr_size * u64::try_from(methods.len()).unwrap().checked_add(3).unwrap(),
59+
// Compute size of vtable, including 3 entries per supertrait for (drop, size, align)
60+
// metadata.
61+
ptr_size * (
62+
methods
63+
.iter()
64+
.map(|l| u64::try_from(methods.len()).unwrap().checked_add(3).unwrap())
65+
.sum()
66+
),
6067
ptr_align,
6168
MemoryKind::Vtable,
6269
);
@@ -76,21 +83,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
7683
let drop = Instance::resolve_drop_in_place(*tcx, ty);
7784
let drop = self.memory.create_fn_alloc(FnVal::Instance(drop));
7885

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
81-
// by multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
82-
write_ptr(&mut self.memory, Scalar::Ptr(drop).into())?;
83-
write_ptr(&mut self.memory, Scalar::from_uint(size, ptr_size).into())?;
84-
write_ptr(&mut self.memory, Scalar::from_uint(align, ptr_size).into())?;
85-
86-
for method in methods.iter() {
87-
if let Some((def_id, substs)) = *method {
88-
// Resolve for vtable; insert shims where needed.
89-
let instance =
90-
ty::Instance::resolve_for_vtable(*tcx, self.param_env, def_id, substs)
91-
.ok_or_else(|| err_inval!(TooGeneric))?;
92-
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
93-
write_ptr(&mut self.memory, Scalar::Ptr(fn_ptr).into())?;
86+
for trait_methods in methods.iter() {
87+
// No need to do any alignment checks on the memory accesses below, because we know the
88+
// allocation is correctly aligned, as we created it above. Also. we're only offsetting
89+
// by multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
90+
write_ptr(&mut self.memory, Scalar::Ptr(drop).into())?;
91+
write_ptr(&mut self.memory, Scalar::from_uint(size, ptr_size).into())?;
92+
write_ptr(&mut self.memory, Scalar::from_uint(align, ptr_size).into())?;
93+
94+
for method in trait_methods.iter() {
95+
if let Some((def_id, substs)) = *method {
96+
// Resolve for vtable; insert shims where needed.
97+
let instance =
98+
ty::Instance::resolve_for_vtable(*tcx, self.param_env, def_id, substs)
99+
.ok_or_else(|| err_inval!(TooGeneric))?;
100+
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
101+
write_ptr(&mut self.memory, Scalar::Ptr(fn_ptr).into())?;
102+
}
94103
}
95104
}
96105

src/librustc_mir/monomorphize/collector.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -882,10 +882,9 @@ fn create_mono_items_for_vtable_methods<'tcx>(
882882
let poly_trait_ref = principal.with_self_ty(tcx, impl_ty);
883883
assert!(!poly_trait_ref.has_escaping_bound_vars());
884884

885-
// Walk all methods of the trait, including those of its supertraits
885+
// Walk all methods of the trait, including those of its supertraits.
886886
let methods = tcx.vtable_methods(poly_trait_ref);
887887
let methods = methods
888-
.iter()
889888
.cloned()
890889
.filter_map(|method| method)
891890
.map(|(def_id, substs)| {

src/librustc_trait_selection/traits/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -462,24 +462,24 @@ fn substitute_normalize_and_test_predicates<'tcx>(
462462
result
463463
}
464464

465-
/// Given a trait `trait_ref`, iterates the vtable entries
466-
/// that come from `trait_ref`, including its supertraits.
465+
/// Given a trait `trait_ref`, iterates the vtable entries that come from `trait_ref`, including its
466+
/// supertraits, and returns them per-trait.
467467
#[inline] // FIXME(#35870): avoid closures being unexported due to `impl Trait`.
468468
fn vtable_methods<'tcx>(
469469
tcx: TyCtxt<'tcx>,
470470
trait_ref: ty::PolyTraitRef<'tcx>,
471-
) -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] {
471+
) -> &'tcx [&'tcx [Option<(DefId, SubstsRef<'tcx>)>]] {
472472
debug!("vtable_methods({:?})", trait_ref);
473473

474-
tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).flat_map(move |trait_ref| {
474+
tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).map(move |trait_ref| {
475475
let trait_methods = tcx
476476
.associated_items(trait_ref.def_id())
477477
.in_definition_order()
478478
.filter(|item| item.kind == ty::AssocKind::Fn);
479479

480-
// Now list each method's DefId and InternalSubsts (for within its trait).
481-
// If the method can never be called from this object, produce None.
482-
trait_methods.map(move |trait_method| {
480+
// Now, list each method's `DefId` and `InternalSubsts` (for within its trait).
481+
// If the method can never be called from this object, produce `None`.
482+
&*tcx.arena.alloc_from_iter(trait_methods.map(move |trait_method| {
483483
debug!("vtable_methods: trait_method={:?}", trait_method);
484484
let def_id = trait_method.def_id;
485485

@@ -516,7 +516,7 @@ fn vtable_methods<'tcx>(
516516
}
517517

518518
Some((def_id, substs))
519-
})
519+
}))
520520
}))
521521
}
522522

0 commit comments

Comments
 (0)