提交 63ed6253 编写于 作者: crlf0710's avatar crlf0710

Implement pointer casting.

上级 7069a8c2
......@@ -25,39 +25,73 @@ pub(crate) fn unsized_info<'tcx>(
.bcx
.ins()
.iconst(fx.pointer_type, len.eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64),
(&ty::Dynamic(..), &ty::Dynamic(..)) => {
// For now, upcasts are limited to changes in marker
// traits, and hence never actually require an actual
// change to the vtable.
old_info.expect("unsized_info: missing old info for trait upcast")
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
let old_info =
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
if data_a.principal_def_id() == data_b.principal_def_id() {
return old_info;
}
// trait upcasting coercion
// if both of the two `principal`s are `None`, this function would have returned early above.
// and if one of the two `principal`s is `None`, typechecking would have rejected this case.
let principal_a = data_a
.principal()
.expect("unsized_info: missing principal trait for trait upcasting coercion");
let principal_b = data_b
.principal()
.expect("unsized_info: missing principal trait for trait upcasting coercion");
let vptr_entry_idx = fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
principal_a.with_self_ty(fx.tcx, source),
principal_b.with_self_ty(fx.tcx, source),
));
if let Some(entry_idx) = vptr_entry_idx {
let entry_idx = u32::try_from(entry_idx).unwrap();
let entry_offset = entry_idx * fx.pointer_type.bytes();
let vptr_ptr = Pointer::new(old_info).offset_i64(fx, entry_offset.into()).load(
fx,
fx.pointer_type,
crate::vtable::vtable_memflags(),
);
vptr_ptr
} else {
old_info
}
}
(_, &ty::Dynamic(ref data, ..)) => crate::vtable::get_vtable(fx, source, data.principal()),
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
}
}
/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer.
fn unsize_thin_ptr<'tcx>(
/// Coerce `src` to `dst_ty`.
fn unsize_ptr<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
src: Value,
src_layout: TyAndLayout<'tcx>,
dst_layout: TyAndLayout<'tcx>,
old_info: Option<Value>,
) -> (Value, Value) {
match (&src_layout.ty.kind(), &dst_layout.ty.kind()) {
(&ty::Ref(_, a, _), &ty::Ref(_, b, _))
| (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
| (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
assert!(!fx.layout_of(a).is_unsized());
(src, unsized_info(fx, a, b, None))
(src, unsized_info(fx, a, b, old_info))
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => {
let (a, b) = (src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty());
assert!(!fx.layout_of(a).is_unsized());
(src, unsized_info(fx, a, b, None))
(src, unsized_info(fx, a, b, old_info))
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
assert_eq!(def_a, def_b);
if src_layout == dst_layout {
return (src, old_info.unwrap());
}
let mut result = None;
for i in 0..src_layout.fields.count() {
let src_f = src_layout.field(fx, i);
......@@ -71,11 +105,11 @@ fn unsize_thin_ptr<'tcx>(
let dst_f = dst_layout.field(fx, i);
assert_ne!(src_f.ty, dst_f.ty);
assert_eq!(result, None);
result = Some(unsize_thin_ptr(fx, src, src_f, dst_f));
result = Some(unsize_ptr(fx, src, src_f, dst_f, old_info));
}
result.unwrap()
}
_ => bug!("unsize_thin_ptr: called on bad types"),
_ => bug!("unsize_ptr: called on bad types"),
}
}
......@@ -91,12 +125,11 @@ pub(crate) fn coerce_unsized_into<'tcx>(
let mut coerce_ptr = || {
let (base, info) =
if fx.layout_of(src.layout().ty.builtin_deref(true).unwrap().ty).is_unsized() {
// fat-ptr to fat-ptr unsize preserves the vtable
// i.e., &'a fmt::Debug+Send => &'a fmt::Debug
src.load_scalar_pair(fx)
let (old_base, old_info) = src.load_scalar_pair(fx);
unsize_ptr(fx, old_base, src.layout(), dst.layout(), Some(old_info))
} else {
let base = src.load_scalar(fx);
unsize_thin_ptr(fx, base, src.layout(), dst.layout())
unsize_ptr(fx, base, src.layout(), dst.layout(), None)
};
dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout()));
};
......
......@@ -5,7 +5,7 @@
use crate::constant::data_id_for_alloc_id;
use crate::prelude::*;
fn vtable_memflags() -> MemFlags {
pub(crate) fn vtable_memflags() -> MemFlags {
let mut flags = MemFlags::trusted(); // A vtable access is always aligned and will never trap.
flags.set_readonly(); // A vtable is always read-only.
flags
......
......@@ -23,7 +23,6 @@
use rustc_middle::middle::lang_items;
use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_session::cgu_reuse_tracker::CguReuse;
......@@ -32,6 +31,7 @@
use rustc_span::symbol::sym;
use rustc_target::abi::{Align, LayoutOf, VariantIdx};
use std::convert::TryFrom;
use std::ops::{Deref, DerefMut};
use std::time::{Duration, Instant};
......@@ -128,55 +128,92 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
///
/// The `old_info` argument is a bit odd. It is intended for use in an upcast,
/// where the new vtable for an object will be derived from the old one.
pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>(
cx: &Cx,
pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
source: Ty<'tcx>,
target: Ty<'tcx>,
old_info: Option<Cx::Value>,
) -> Cx::Value {
old_info: Option<Bx::Value>,
) -> Bx::Value {
let cx = bx.cx();
let (source, target) =
cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, cx.param_env());
cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, bx.param_env());
match (source.kind(), target.kind()) {
(&ty::Array(_, len), &ty::Slice(_)) => {
cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
}
(&ty::Dynamic(..), &ty::Dynamic(..)) => {
// For now, upcasts are limited to changes in marker
// traits, and hence never actually require an actual
// change to the vtable.
old_info.expect("unsized_info: missing old info for trait upcast")
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
let old_info =
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
if data_a.principal_def_id() == data_b.principal_def_id() {
return old_info;
}
// trait upcasting coercion
// if both of the two `principal`s are `None`, this function would have returned early above.
// and if one of the two `principal`s is `None`, typechecking would have rejected this case.
let principal_a = data_a
.principal()
.expect("unsized_info: missing principal trait for trait upcasting coercion");
let principal_b = data_b
.principal()
.expect("unsized_info: missing principal trait for trait upcasting coercion");
let vptr_entry_idx = cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((
principal_a.with_self_ty(cx.tcx(), source),
principal_b.with_self_ty(cx.tcx(), source),
));
if let Some(entry_idx) = vptr_entry_idx {
let ptr_ty = cx.type_i8p();
let ptr_align = cx.tcx().data_layout.pointer_align.abi;
let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
let gep =
bx.inbounds_gep(llvtable, &[bx.const_usize(u64::try_from(entry_idx).unwrap())]);
let new_vptr = bx.load(ptr_ty, gep, ptr_align);
bx.nonnull_metadata(new_vptr);
// Vtable loads are invariant.
bx.set_invariant_load(new_vptr);
new_vptr
} else {
old_info
}
}
(_, &ty::Dynamic(ref data, ..)) => {
let vtable_ptr = cx.layout_of(cx.tcx().mk_mut_ptr(target)).field(cx, FAT_PTR_EXTRA);
cx.const_ptrcast(
meth::get_vtable(cx, source, data.principal()),
cx.backend_type(vtable_ptr),
)
let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
1,
true,
);
cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
}
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
}
}
/// Coerces `src` to `dst_ty`. `src_ty` must be a thin pointer.
pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
/// Coerces `src` to `dst_ty`. `src_ty` must be a pointer.
pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
src: Bx::Value,
src_ty: Ty<'tcx>,
dst_ty: Ty<'tcx>,
old_info: Option<Bx::Value>,
) -> (Bx::Value, Bx::Value) {
debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty);
debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty);
match (src_ty.kind(), dst_ty.kind()) {
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
| (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
assert!(bx.cx().type_is_sized(a));
assert_eq!(bx.cx().type_is_sized(a), old_info.is_none());
let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b)));
(bx.pointercast(src, ptr_ty), unsized_info(bx.cx(), a, b, None))
(bx.pointercast(src, ptr_ty), unsized_info(bx, a, b, old_info))
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
assert_eq!(def_a, def_b);
let src_layout = bx.cx().layout_of(src_ty);
let dst_layout = bx.cx().layout_of(dst_ty);
if src_ty == dst_ty {
return (src, old_info.unwrap());
}
let mut result = None;
for i in 0..src_layout.fields.count() {
let src_f = src_layout.field(bx.cx(), i);
......@@ -190,18 +227,15 @@ pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let dst_f = dst_layout.field(bx.cx(), i);
assert_ne!(src_f.ty, dst_f.ty);
assert_eq!(result, None);
result = Some(unsize_thin_ptr(bx, src, src_f.ty, dst_f.ty));
result = Some(unsize_ptr(bx, src, src_f.ty, dst_f.ty, old_info));
}
let (lldata, llextra) = result.unwrap();
let lldata_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true);
let llextra_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true);
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
// FIXME(eddyb) move these out of this `match` arm, so they're always
// applied, uniformly, no matter the source/destination types.
(
bx.bitcast(lldata, bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true)),
bx.bitcast(llextra, bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true)),
)
(bx.bitcast(lldata, lldata_ty), bx.bitcast(llextra, llextra_ty))
}
_ => bug!("unsize_thin_ptr: called on bad types"),
_ => bug!("unsize_ptr: called on bad types"),
}
}
......@@ -217,17 +251,8 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
match (src_ty.kind(), dst_ty.kind()) {
(&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => {
let (base, info) = match bx.load_operand(src).val {
OperandValue::Pair(base, info) => {
// fat-ptr to fat-ptr unsize preserves the vtable
// i.e., &'a fmt::Debug+Send => &'a fmt::Debug
// So we need to pointercast the base to ensure
// the types match up.
// FIXME(eddyb) use `scalar_pair_element_backend_type` here,
// like `unsize_thin_ptr` does.
let thin_ptr = dst.layout.field(bx.cx(), FAT_PTR_ADDR);
(bx.pointercast(base, bx.cx().backend_type(thin_ptr)), info)
}
OperandValue::Immediate(base) => unsize_thin_ptr(bx, base, src_ty, dst_ty),
OperandValue::Pair(base, info) => unsize_ptr(bx, base, src_ty, dst_ty, Some(info)),
OperandValue::Immediate(base) => unsize_ptr(bx, base, src_ty, dst_ty, None),
OperandValue::Ref(..) => bug!(),
};
OperandValue::Pair(base, info).store(bx, dst);
......
......@@ -220,34 +220,23 @@ pub fn codegen_rvalue_operand(
}
mir::CastKind::Pointer(PointerCast::Unsize) => {
assert!(bx.cx().is_backend_scalar_pair(cast));
match operand.val {
let (lldata, llextra) = match operand.val {
OperandValue::Pair(lldata, llextra) => {
// unsize from a fat pointer -- this is a
// "trait-object-to-supertrait" coercion, for
// example, `&'a fmt::Debug + Send => &'a fmt::Debug`.
// HACK(eddyb) have to bitcast pointers
// until LLVM removes pointee types.
let lldata = bx.pointercast(
lldata,
bx.cx().scalar_pair_element_backend_type(cast, 0, true),
);
OperandValue::Pair(lldata, llextra)
// "trait-object-to-supertrait" coercion.
(lldata, Some(llextra))
}
OperandValue::Immediate(lldata) => {
// "standard" unsize
let (lldata, llextra) = base::unsize_thin_ptr(
&mut bx,
lldata,
operand.layout.ty,
cast.ty,
);
OperandValue::Pair(lldata, llextra)
(lldata, None)
}
OperandValue::Ref(..) => {
bug!("by-ref operand {:?} in `codegen_rvalue_operand`", operand);
}
}
};
let (lldata, llextra) =
base::unsize_ptr(&mut bx, lldata, operand.layout.ty, cast.ty, llextra);
OperandValue::Pair(lldata, llextra)
}
mir::CastKind::Pointer(PointerCast::MutToConstPointer)
| mir::CastKind::Misc
......
......@@ -977,6 +977,11 @@
desc { |tcx| "finding all vtable entries for trait {}", tcx.def_path_str(key.def_id()) }
}
query vtable_trait_upcasting_coercion_new_vptr_slot(key: (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>)) -> Option<usize> {
desc { |tcx| "finding the slot within vtable for trait {} vtable ptr during trait upcasting coercion from {} vtable",
tcx.def_path_str(key.1.def_id()), tcx.def_path_str(key.0.def_id()) }
}
query codegen_fulfill_obligation(
key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
) -> Result<ImplSource<'tcx, ()>, ErrorReported> {
......
......@@ -269,12 +269,34 @@ fn unsize_into_ptr(
Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self);
self.write_immediate(val, dest)
}
(&ty::Dynamic(..), &ty::Dynamic(..)) => {
// For now, upcasts are limited to changes in marker
// traits, and hence never actually require an actual
// change to the vtable.
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
let val = self.read_immediate(src)?;
self.write_immediate(*val, dest)
if data_a.principal_def_id() == data_b.principal_def_id() {
return self.write_immediate(*val, dest);
}
// trait upcasting coercion
let principal_a = data_a.principal().expect(
"unsize_into_ptr: missing principal trait for trait upcasting coercion",
);
let principal_b = data_b.principal().expect(
"unsize_into_ptr: missing principal trait for trait upcasting coercion",
);
let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
principal_a.with_self_ty(*self.tcx, src_pointee_ty),
principal_b.with_self_ty(*self.tcx, src_pointee_ty),
));
if let Some(entry_idx) = vptr_entry_idx {
let entry_idx = u64::try_from(entry_idx).unwrap();
let (old_data, old_vptr) = val.to_scalar_pair()?;
let old_vptr = self.scalar_to_ptr(old_vptr);
let new_vptr = self
.read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?;
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
} else {
self.write_immediate(*val, dest)
}
}
(_, &ty::Dynamic(ref data, _)) => {
// Initial cast from sized to dyn trait
......
......@@ -79,6 +79,16 @@ pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Tag> {
pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Tag>> {
self.to_scalar_or_uninit().check_init()
}
#[inline]
pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
match self {
Immediate::ScalarPair(val1, val2) => Ok((val1.check_init()?, val2.check_init()?)),
Immediate::Scalar(..) => {
bug!("Got a scalar where a wide pointer was expected")
}
}
}
}
// ScalarPair needs a type to interpret, so we often have an immediate and a type together
......
......@@ -121,4 +121,27 @@ pub fn read_size_and_align_from_vtable(
}
Ok((Size::from_bytes(size), align))
}
pub fn read_new_vtable_after_trait_upcasting_from_vtable(
&self,
vtable: Pointer<Option<M::PointerTag>>,
idx: u64,
) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
let pointer_size = self.pointer_size();
let vtable = self
.memory
.get(
vtable,
pointer_size * idx.checked_add(1).unwrap(),
self.tcx.data_layout.pointer_align.abi,
)?
.expect("cannot be a ZST");
let new_vtable = self
.scalar_to_ptr(vtable.read_ptr_sized(pointer_size * idx)?.check_init()?)
.into_pointer_or_addr()
.expect("should be a pointer");
Ok(new_vtable)
}
}
......@@ -282,6 +282,16 @@ fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
}
}
impl<'tcx> Key for (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>) {
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.0.def_id().krate == LOCAL_CRATE
}
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
tcx.def_span(self.0.def_id())
}
}
impl<'tcx> Key for GenericArg<'tcx> {
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
......
......@@ -756,16 +756,17 @@ fn vtable_trait_first_method_offset<'tcx>(
}
/// Find slot offset for trait vptr within vtable entries of another trait
/// FIXME: This function is not yet used. Remove `#[allow(dead_code)]` when it's used in upcoming pr.
#[allow(dead_code)]
fn vtable_trait_vptr_slot_offset<'tcx>(
pub fn vtable_trait_upcasting_coercion_new_vptr_slot(
tcx: TyCtxt<'tcx>,
key: (
ty::PolyTraitRef<'tcx>, // trait_to_be_found
ty::PolyTraitRef<'tcx>, // trait_owning_vtable
ty::PolyTraitRef<'tcx>, // trait owning vtable
ty::PolyTraitRef<'tcx>, // super trait ref
),
) -> Option<usize> {
let (trait_to_be_found, trait_owning_vtable) = key;
let (trait_owning_vtable, super_trait_ref) = key;
let super_trait_did = super_trait_ref.def_id();
// FIXME: take substsref part into account here after upcasting coercion allows the same def_id occur
// multiple times.
let vtable_segment_callback = {
let mut vptr_offset = 0;
......@@ -776,7 +777,7 @@ fn vtable_trait_vptr_slot_offset<'tcx>(
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
vptr_offset += util::count_own_vtable_entries(tcx, trait_ref);
if trait_ref == trait_to_be_found {
if trait_ref.def_id() == super_trait_did {
if emit_vptr {
return ControlFlow::Break(Some(vptr_offset));
} else {
......@@ -810,6 +811,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
specializes: specialize::specializes,
codegen_fulfill_obligation: codegen::codegen_fulfill_obligation,
vtable_entries,
vtable_trait_upcasting_coercion_new_vptr_slot,
subst_and_check_impossible_predicates,
mir_abstract_const: |tcx, def_id| {
let def_id = def_id.expect_local();
......
// run-pass
#![feature(trait_upcasting)]
#![allow(incomplete_features)]
trait A {
fn foo_a(&self);
}
trait B {
fn foo_b(&self);
}
trait C: A + B {
fn foo_c(&self);
}
struct S(i32);
impl A for S {
fn foo_a(&self) {
unreachable!();
}
}
impl B for S {
fn foo_b(&self) {
assert_eq!(42, self.0);
}
}
impl C for S {
fn foo_c(&self) {
unreachable!();
}
}
fn invoke_inner(b: &dyn B) {
b.foo_b();
}
fn invoke_outer(c: &dyn C) {
invoke_inner(c);
}
fn main() {
let s = S(42);
invoke_outer(&s);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册