diff --git a/src/librustc_mir/tcx/mod.rs b/src/librustc_mir/tcx/mod.rs index 3b9d9228a16..69240e9f995 100644 --- a/src/librustc_mir/tcx/mod.rs +++ b/src/librustc_mir/tcx/mod.rs @@ -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> { @@ -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, + } + } +} diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 7f258cb845b..8023f776dde 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -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}; @@ -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, + 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, diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index ac3e3beeac8..275f4628788 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -1223,4 +1223,4 @@ pub fn get_static_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } else { base::get_extern_const(ccx, did, ty) } -} +} \ No newline at end of file diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index b849888cf7c..91f17a50e2c 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -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() { diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index eb9635066f3..728b53dcd4a 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -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, - 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() { diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index f6aa79393d3..ff80451d2b1 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -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); diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index f933252b51e..8f5496929c2 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -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) => { @@ -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); + + 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::Cast(..) => { - unimplemented!() - } - - 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, }) } } diff --git a/src/test/run-pass/mir_coercions.rs b/src/test/run-pass/mir_coercions.rs new file mode 100644 index 00000000000..c1897f79f22 --- /dev/null +++ b/src/test/run-pass/mir_coercions.rs @@ -0,0 +1,76 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs, coerce_unsized, unsize)] + +use std::ops::CoerceUnsized; +use std::marker::Unsize; + +#[rustc_mir] +fn identity_coercion(x: &(Fn(u32)->u32 + Send)) -> &Fn(u32)->u32 { + x +} +#[rustc_mir] +fn fn_coercions(f: &fn(u32) -> u32) -> + (unsafe fn(u32) -> u32, + &(Fn(u32) -> u32+Send)) +{ + (*f, f) +} + +#[rustc_mir] +fn simple_array_coercion(x: &[u8; 3]) -> &[u8] { x } + +fn square(a: u32) -> u32 { a * a } + +#[derive(PartialEq,Eq)] +struct PtrWrapper<'a, T: 'a+?Sized>(u32, u32, (), &'a T); +impl<'a, T: ?Sized+Unsize, U: ?Sized> + CoerceUnsized> for PtrWrapper<'a, T> {} + +struct TrivPtrWrapper<'a, T: 'a+?Sized>(&'a T); +impl<'a, T: ?Sized+Unsize, U: ?Sized> + CoerceUnsized> for TrivPtrWrapper<'a, T> {} + +#[rustc_mir] +fn coerce_ptr_wrapper(p: PtrWrapper<[u8; 3]>) -> PtrWrapper<[u8]> { + p +} + +#[rustc_mir] +fn coerce_triv_ptr_wrapper(p: TrivPtrWrapper<[u8; 3]>) -> TrivPtrWrapper<[u8]> { + p +} + +#[rustc_mir] +fn coerce_fat_ptr_wrapper(p: PtrWrapper u32+Send>) + -> PtrWrapper u32> { + p +} + + +fn main() { + let a = [0,1,2]; + let square_local : fn(u32) -> u32 = square; + let (f,g) = fn_coercions(&square_local); + assert_eq!(f as usize, square as usize); + assert_eq!(g(4), 16); + assert_eq!(identity_coercion(g)(5), 25); + + assert_eq!(simple_array_coercion(&a), &a); + let w = coerce_ptr_wrapper(PtrWrapper(2,3,(),&a)); + assert!(w == PtrWrapper(2,3,(),&a) as PtrWrapper<[u8]>); + + let w = coerce_triv_ptr_wrapper(TrivPtrWrapper(&a)); + assert_eq!(&w.0, &a); + + let z = coerce_fat_ptr_wrapper(PtrWrapper(2,3,(),&square_local)); + assert_eq!((z.3)(6), 36); +}