mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-12 08:36:03 +00:00
implement coercions in MIR
This commit is contained in:
parent
cd1585ffb3
commit
e82f5d4f54
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<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,
|
||||
|
@ -1223,4 +1223,4 @@ pub fn get_static_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
} else {
|
||||
base::get_extern_const(ccx, did, ty)
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
76
src/test/run-pass/mir_coercions.rs
Normal file
76
src/test/run-pass/mir_coercions.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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>, U: ?Sized>
|
||||
CoerceUnsized<PtrWrapper<'a, U>> for PtrWrapper<'a, T> {}
|
||||
|
||||
struct TrivPtrWrapper<'a, T: 'a+?Sized>(&'a T);
|
||||
impl<'a, T: ?Sized+Unsize<U>, U: ?Sized>
|
||||
CoerceUnsized<TrivPtrWrapper<'a, U>> 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<Fn(u32) -> u32+Send>)
|
||||
-> PtrWrapper<Fn(u32) -> 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user