Skip to content

Commit

Permalink
implement coercions in MIR
Browse files Browse the repository at this point in the history
  • Loading branch information
arielb1 committed Nov 13, 2015
1 parent cd1585f commit e82f5d4
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 59 deletions.
15 changes: 15 additions & 0 deletions src/librustc_mir/tcx/mod.rs
Expand Up @@ -16,6 +16,7 @@
use repr::*;
use rustc::middle::subst::Substs;
use rustc::middle::ty::{self, AdtDef, Ty};
use rustc_front::hir;

#[derive(Copy, Clone, Debug)]
pub enum LvalueTy<'tcx> {
Expand Down Expand Up @@ -123,3 +124,17 @@ impl<'tcx> Mir<'tcx> {
}
}
}

impl BorrowKind {
pub fn to_mutbl_lossy(self) -> hir::Mutability {
match self {
BorrowKind::Mut => hir::MutMutable,
BorrowKind::Shared => hir::MutImmutable,

// We have no type corresponding to a unique imm borrow, so
// use `&mut`. It gives all the capabilities of an `&uniq`
// and hence is a safe "over approximation".
BorrowKind::Unique => hir::MutMutable,
}
}
}
125 changes: 124 additions & 1 deletion src/librustc_trans/trans/base.rs
Expand Up @@ -55,7 +55,7 @@ use trans::builder::{Builder, noname};
use trans::callee;
use trans::cleanup::{self, CleanupMethods, DropHint};
use trans::closure;
use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_integral};
use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_uint, C_integral};
use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
use trans::common::{CrateContext, DropFlagHintsMap, Field, FunctionContext};
use trans::common::{Result, NodeIdAndSpan, VariantInfo};
Expand Down Expand Up @@ -577,6 +577,129 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
return cx;
}


/// Retrieve the information we are losing (making dynamic) in an unsizing
/// adjustment.
///
/// The `old_info` argument is a bit funny. It is intended for use
/// in an upcast, where the new vtable for an object will be drived
/// from the old one.
pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>,
source: Ty<'tcx>,
target: Ty<'tcx>,
old_info: Option<ValueRef>,
param_substs: &'tcx Substs<'tcx>)
-> ValueRef {
let (source, target) = ccx.tcx().struct_lockstep_tails(source, target);
match (&source.sty, &target.sty) {
(&ty::TyArray(_, len), &ty::TySlice(_)) => C_uint(ccx, len),
(&ty::TyTrait(_), &ty::TyTrait(_)) => {
// 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::TyTrait(box ty::TraitTy { ref principal, .. })) => {
// Note that we preserve binding levels here:
let substs = principal.0.substs.with_self_ty(source).erase_regions();
let substs = ccx.tcx().mk_substs(substs);
let trait_ref = ty::Binder(ty::TraitRef { def_id: principal.def_id(),
substs: substs });
consts::ptrcast(meth::get_vtable(ccx, trait_ref, param_substs),
Type::vtable_ptr(ccx))
}
_ => ccx.sess().bug(&format!("unsized_info: invalid unsizing {:?} -> {:?}",
source,
target))
}
}

/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer.
pub fn unsize_thin_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
src: ValueRef,
src_ty: Ty<'tcx>,
dst_ty: Ty<'tcx>)
-> (ValueRef, ValueRef) {
debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty);
match (&src_ty.sty, &dst_ty.sty) {
(&ty::TyBox(a), &ty::TyBox(b)) |
(&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }),
&ty::TyRef(_, ty::TypeAndMut { ty: b, .. })) |
(&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }),
&ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) |
(&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }),
&ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => {
assert!(common::type_is_sized(bcx.tcx(), a));
let ptr_ty = type_of::in_memory_type_of(bcx.ccx(), b).ptr_to();
(PointerCast(bcx, src, ptr_ty),
unsized_info(bcx.ccx(), a, b, None, bcx.fcx.param_substs))
}
_ => bcx.sess().bug(
&format!("unsize_thin_ptr: called on bad types"))
}
}

/// Coerce `src`, which is a reference to a value of type `src_ty`,
/// to a value of type `dst_ty` and store the result in `dst`
pub fn coerce_unsized_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
src: ValueRef,
src_ty: Ty<'tcx>,
dst: ValueRef,
dst_ty: Ty<'tcx>) {
match (&src_ty.sty, &dst_ty.sty) {
(&ty::TyBox(..), &ty::TyBox(..)) |
(&ty::TyRef(..), &ty::TyRef(..)) |
(&ty::TyRef(..), &ty::TyRawPtr(..)) |
(&ty::TyRawPtr(..), &ty::TyRawPtr(..)) => {
let (base, info) = if common::type_is_fat_ptr(bcx.tcx(), src_ty) {
// fat-ptr to fat-ptr unsize preserves the vtable
load_fat_ptr(bcx, src, src_ty)
} else {
let base = load_ty(bcx, src, src_ty);
unsize_thin_ptr(bcx, base, src_ty, dst_ty)
};
store_fat_ptr(bcx, base, info, dst, dst_ty);
}

// This can be extended to enums and tuples in the future.
// (&ty::TyEnum(def_id_a, _), &ty::TyEnum(def_id_b, _)) |
(&ty::TyStruct(def_a, _), &ty::TyStruct(def_b, _)) => {
assert_eq!(def_a, def_b);

let src_repr = adt::represent_type(bcx.ccx(), src_ty);
let src_fields = match &*src_repr {
&adt::Repr::Univariant(ref s, _) => &s.fields,
_ => bcx.sess().bug("struct has non-univariant repr")
};
let dst_repr = adt::represent_type(bcx.ccx(), dst_ty);
let dst_fields = match &*dst_repr {
&adt::Repr::Univariant(ref s, _) => &s.fields,
_ => bcx.sess().bug("struct has non-univariant repr")
};

let iter = src_fields.iter().zip(dst_fields).enumerate();
for (i, (src_fty, dst_fty)) in iter {
if type_is_zero_size(bcx.ccx(), dst_fty) { continue; }

let src_f = adt::trans_field_ptr(bcx, &src_repr, src, 0, i);
let dst_f = adt::trans_field_ptr(bcx, &dst_repr, dst, 0, i);
if src_fty == dst_fty {
memcpy_ty(bcx, dst_f, src_f, src_fty);
} else {
coerce_unsized_into(
bcx,
src_f, src_fty,
dst_f, dst_fty
);
}
}
}
_ => bcx.sess().bug(&format!("coerce_unsized_into: invalid coercion {:?} -> {:?}",
src_ty,
dst_ty))
}
}

pub fn cast_shift_expr_rhs(cx: Block,
op: hir::BinOp_,
lhs: ValueRef,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/common.rs
Expand Up @@ -1223,4 +1223,4 @@ pub fn get_static_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
} else {
base::get_extern_const(ccx, did, ty)
}
}
}
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/consts.rs
Expand Up @@ -410,7 +410,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
.expect("consts: unsizing got non-pointer target type").ty;
let ptr_ty = type_of::in_memory_type_of(cx, unsized_ty).ptr_to();
let base = ptrcast(base, ptr_ty);
let info = expr::unsized_info(cx, pointee_ty, unsized_ty,
let info = base::unsized_info(cx, pointee_ty, unsized_ty,
old_info, param_substs);

if old_info.is_none() {
Expand Down
36 changes: 0 additions & 36 deletions src/librustc_trans/trans/expr.rs
Expand Up @@ -326,42 +326,6 @@ pub fn copy_fat_ptr(bcx: Block, src_ptr: ValueRef, dst_ptr: ValueRef) {
Store(bcx, Load(bcx, get_meta(bcx, src_ptr)), get_meta(bcx, dst_ptr));
}

/// Retrieve the information we are losing (making dynamic) in an unsizing
/// adjustment.
///
/// The `old_info` argument is a bit funny. It is intended for use
/// in an upcast, where the new vtable for an object will be drived
/// from the old one.
pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>,
source: Ty<'tcx>,
target: Ty<'tcx>,
old_info: Option<ValueRef>,
param_substs: &'tcx Substs<'tcx>)
-> ValueRef {
let (source, target) = ccx.tcx().struct_lockstep_tails(source, target);
match (&source.sty, &target.sty) {
(&ty::TyArray(_, len), &ty::TySlice(_)) => C_uint(ccx, len),
(&ty::TyTrait(_), &ty::TyTrait(_)) => {
// 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::TyTrait(box ty::TraitTy { ref principal, .. })) => {
// Note that we preserve binding levels here:
let substs = principal.0.substs.with_self_ty(source).erase_regions();
let substs = ccx.tcx().mk_substs(substs);
let trait_ref = ty::Binder(ty::TraitRef { def_id: principal.def_id(),
substs: substs });
consts::ptrcast(meth::get_vtable(ccx, trait_ref, param_substs),
Type::vtable_ptr(ccx))
}
_ => ccx.sess().bug(&format!("unsized_info: invalid unsizing {:?} -> {:?}",
source,
target))
}
}

fn adjustment_required<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
expr: &hir::Expr) -> bool {
let adjustment = match bcx.tcx().tables.borrow().adjustments.get(&expr.id).cloned() {
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_trans/trans/mir/lvalue.rs
Expand Up @@ -85,7 +85,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::Lvalue::Arg(index) => self.args[index as usize],
mir::Lvalue::Static(def_id) => {
let const_ty = self.mir.lvalue_ty(tcx, lvalue);
LvalueRef::new(common::get_static_val(ccx, def_id, const_ty.to_ty(tcx)), const_ty)
LvalueRef::new_sized(
common::get_static_val(ccx, def_id, const_ty.to_ty(tcx)),
const_ty)
},
mir::Lvalue::ReturnPointer => {
let return_ty = bcx.monomorphize(&self.mir.return_ty);
Expand Down
97 changes: 78 additions & 19 deletions src/librustc_trans/trans/mir/rvalue.rs
Expand Up @@ -46,20 +46,35 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}

mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, cast_ty) => {
let expr_ty =
bcx.monomorphize(&self.mir.operand_ty(bcx.tcx(), operand));
let cast_ty =
bcx.monomorphize(&cast_ty);
if expr_ty == cast_ty {
debug!("trans_rvalue: trivial unsize at {:?}", expr_ty);
self.trans_operand_into(bcx, lldest, operand);
if common::type_is_fat_ptr(bcx.tcx(), cast_ty) {
let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
self.store_operand(bcx, lldest, temp);
return bcx;
}
unimplemented!()
}

mir::Rvalue::Cast(..) => {
unimplemented!()
// Unsize of a nontrivial struct. I would prefer for
// this to be eliminated by MIR translation, but
// `CoerceUnsized` can be passed by a where-clause,
// so the (generic) MIR may not be able to expand it.
let operand = self.trans_operand(bcx, operand);
match operand.val {
OperandValue::FatPtr(..) => unreachable!(),
OperandValue::Imm(llval) => {
// ugly alloca.
debug!("trans_rvalue: creating ugly alloca");
let lltemp = base::alloc_ty(bcx, operand.ty, "__unsize_temp");
base::store_ty(bcx, llval, lltemp, operand.ty);
base::coerce_unsized_into(bcx,
lltemp, operand.ty,
lldest, cast_ty);
}
OperandValue::Ref(llref) => {
base::coerce_unsized_into(bcx,
llref, operand.ty,
lldest, cast_ty);
}
}
bcx
}

mir::Rvalue::Repeat(ref elem, ref count) => {
Expand Down Expand Up @@ -125,30 +140,74 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
(bcx, operand)
}

mir::Rvalue::Cast(mir::CastKind::Unsize, _, _) => {
unimplemented!()
}
mir::Rvalue::Cast(ref kind, ref operand, cast_ty) => {
let operand = self.trans_operand(bcx, operand);
debug!("cast operand is {}", operand.repr(bcx));
let cast_ty = bcx.monomorphize(&cast_ty);

mir::Rvalue::Cast(..) => {
unimplemented!()
let val = match *kind {
mir::CastKind::ReifyFnPointer |
mir::CastKind::UnsafeFnPointer => {
// these are no-ops at the LLVM level
operand.val
}
mir::CastKind::Unsize => {
// unsize targets other than to a fat pointer currently
// can't be operands.
assert!(common::type_is_fat_ptr(bcx.tcx(), cast_ty));

match operand.val {
OperandValue::FatPtr(..) => {
// unsize from a fat pointer - this is a
// "trait-object-to-supertrait" coercion, for
// example,
// &'a fmt::Debug+Send => &'a fmt::Debug,
// and is a no-op at the LLVM level
operand.val
}
OperandValue::Imm(lldata) => {
// "standard" unsize
let (lldata, llextra) =
base::unsize_thin_ptr(bcx, lldata,
operand.ty, cast_ty);
OperandValue::FatPtr(lldata, llextra)
}
OperandValue::Ref(_) => {
bcx.sess().bug(
&format!("by-ref operand {} in trans_rvalue_operand",
operand.repr(bcx)));
}
}
}
mir::CastKind::Misc => unimplemented!()
};
(bcx, OperandRef {
val: val,
ty: cast_ty
})
}

mir::Rvalue::Ref(_, _, ref lvalue) => {
mir::Rvalue::Ref(_, bk, ref lvalue) => {
let tr_lvalue = self.trans_lvalue(bcx, lvalue);

let ty = tr_lvalue.ty.to_ty(bcx.tcx());
let ref_ty = bcx.tcx().mk_ref(
bcx.tcx().mk_region(ty::ReStatic),
ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() }
);

// Note: lvalues are indirect, so storing the `llval` into the
// destination effectively creates a reference.
if common::type_is_sized(bcx.tcx(), ty) {
(bcx, OperandRef {
val: OperandValue::Imm(tr_lvalue.llval),
ty: ty,
ty: ref_ty,
})
} else {
(bcx, OperandRef {
val: OperandValue::FatPtr(tr_lvalue.llval,
tr_lvalue.llextra),
ty: ty,
ty: ref_ty,
})
}
}
Expand Down

0 comments on commit e82f5d4

Please sign in to comment.