Skip to content

Commit e82f5d4

Browse files
committed
implement coercions in MIR
1 parent cd1585f commit e82f5d4

File tree

8 files changed

+298
-59
lines changed

8 files changed

+298
-59
lines changed

src/librustc_mir/tcx/mod.rs

+15
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use repr::*;
1717
use rustc::middle::subst::Substs;
1818
use rustc::middle::ty::{self, AdtDef, Ty};
19+
use rustc_front::hir;
1920

2021
#[derive(Copy, Clone, Debug)]
2122
pub enum LvalueTy<'tcx> {
@@ -123,3 +124,17 @@ impl<'tcx> Mir<'tcx> {
123124
}
124125
}
125126
}
127+
128+
impl BorrowKind {
129+
pub fn to_mutbl_lossy(self) -> hir::Mutability {
130+
match self {
131+
BorrowKind::Mut => hir::MutMutable,
132+
BorrowKind::Shared => hir::MutImmutable,
133+
134+
// We have no type corresponding to a unique imm borrow, so
135+
// use `&mut`. It gives all the capabilities of an `&uniq`
136+
// and hence is a safe "over approximation".
137+
BorrowKind::Unique => hir::MutMutable,
138+
}
139+
}
140+
}

src/librustc_trans/trans/base.rs

+124-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use trans::builder::{Builder, noname};
5555
use trans::callee;
5656
use trans::cleanup::{self, CleanupMethods, DropHint};
5757
use trans::closure;
58-
use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_integral};
58+
use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_uint, C_integral};
5959
use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
6060
use trans::common::{CrateContext, DropFlagHintsMap, Field, FunctionContext};
6161
use trans::common::{Result, NodeIdAndSpan, VariantInfo};
@@ -577,6 +577,129 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
577577
return cx;
578578
}
579579

580+
581+
/// Retrieve the information we are losing (making dynamic) in an unsizing
582+
/// adjustment.
583+
///
584+
/// The `old_info` argument is a bit funny. It is intended for use
585+
/// in an upcast, where the new vtable for an object will be drived
586+
/// from the old one.
587+
pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>,
588+
source: Ty<'tcx>,
589+
target: Ty<'tcx>,
590+
old_info: Option<ValueRef>,
591+
param_substs: &'tcx Substs<'tcx>)
592+
-> ValueRef {
593+
let (source, target) = ccx.tcx().struct_lockstep_tails(source, target);
594+
match (&source.sty, &target.sty) {
595+
(&ty::TyArray(_, len), &ty::TySlice(_)) => C_uint(ccx, len),
596+
(&ty::TyTrait(_), &ty::TyTrait(_)) => {
597+
// For now, upcasts are limited to changes in marker
598+
// traits, and hence never actually require an actual
599+
// change to the vtable.
600+
old_info.expect("unsized_info: missing old info for trait upcast")
601+
}
602+
(_, &ty::TyTrait(box ty::TraitTy { ref principal, .. })) => {
603+
// Note that we preserve binding levels here:
604+
let substs = principal.0.substs.with_self_ty(source).erase_regions();
605+
let substs = ccx.tcx().mk_substs(substs);
606+
let trait_ref = ty::Binder(ty::TraitRef { def_id: principal.def_id(),
607+
substs: substs });
608+
consts::ptrcast(meth::get_vtable(ccx, trait_ref, param_substs),
609+
Type::vtable_ptr(ccx))
610+
}
611+
_ => ccx.sess().bug(&format!("unsized_info: invalid unsizing {:?} -> {:?}",
612+
source,
613+
target))
614+
}
615+
}
616+
617+
/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer.
618+
pub fn unsize_thin_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
619+
src: ValueRef,
620+
src_ty: Ty<'tcx>,
621+
dst_ty: Ty<'tcx>)
622+
-> (ValueRef, ValueRef) {
623+
debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty);
624+
match (&src_ty.sty, &dst_ty.sty) {
625+
(&ty::TyBox(a), &ty::TyBox(b)) |
626+
(&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }),
627+
&ty::TyRef(_, ty::TypeAndMut { ty: b, .. })) |
628+
(&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }),
629+
&ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) |
630+
(&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }),
631+
&ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => {
632+
assert!(common::type_is_sized(bcx.tcx(), a));
633+
let ptr_ty = type_of::in_memory_type_of(bcx.ccx(), b).ptr_to();
634+
(PointerCast(bcx, src, ptr_ty),
635+
unsized_info(bcx.ccx(), a, b, None, bcx.fcx.param_substs))
636+
}
637+
_ => bcx.sess().bug(
638+
&format!("unsize_thin_ptr: called on bad types"))
639+
}
640+
}
641+
642+
/// Coerce `src`, which is a reference to a value of type `src_ty`,
643+
/// to a value of type `dst_ty` and store the result in `dst`
644+
pub fn coerce_unsized_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
645+
src: ValueRef,
646+
src_ty: Ty<'tcx>,
647+
dst: ValueRef,
648+
dst_ty: Ty<'tcx>) {
649+
match (&src_ty.sty, &dst_ty.sty) {
650+
(&ty::TyBox(..), &ty::TyBox(..)) |
651+
(&ty::TyRef(..), &ty::TyRef(..)) |
652+
(&ty::TyRef(..), &ty::TyRawPtr(..)) |
653+
(&ty::TyRawPtr(..), &ty::TyRawPtr(..)) => {
654+
let (base, info) = if common::type_is_fat_ptr(bcx.tcx(), src_ty) {
655+
// fat-ptr to fat-ptr unsize preserves the vtable
656+
load_fat_ptr(bcx, src, src_ty)
657+
} else {
658+
let base = load_ty(bcx, src, src_ty);
659+
unsize_thin_ptr(bcx, base, src_ty, dst_ty)
660+
};
661+
store_fat_ptr(bcx, base, info, dst, dst_ty);
662+
}
663+
664+
// This can be extended to enums and tuples in the future.
665+
// (&ty::TyEnum(def_id_a, _), &ty::TyEnum(def_id_b, _)) |
666+
(&ty::TyStruct(def_a, _), &ty::TyStruct(def_b, _)) => {
667+
assert_eq!(def_a, def_b);
668+
669+
let src_repr = adt::represent_type(bcx.ccx(), src_ty);
670+
let src_fields = match &*src_repr {
671+
&adt::Repr::Univariant(ref s, _) => &s.fields,
672+
_ => bcx.sess().bug("struct has non-univariant repr")
673+
};
674+
let dst_repr = adt::represent_type(bcx.ccx(), dst_ty);
675+
let dst_fields = match &*dst_repr {
676+
&adt::Repr::Univariant(ref s, _) => &s.fields,
677+
_ => bcx.sess().bug("struct has non-univariant repr")
678+
};
679+
680+
let iter = src_fields.iter().zip(dst_fields).enumerate();
681+
for (i, (src_fty, dst_fty)) in iter {
682+
if type_is_zero_size(bcx.ccx(), dst_fty) { continue; }
683+
684+
let src_f = adt::trans_field_ptr(bcx, &src_repr, src, 0, i);
685+
let dst_f = adt::trans_field_ptr(bcx, &dst_repr, dst, 0, i);
686+
if src_fty == dst_fty {
687+
memcpy_ty(bcx, dst_f, src_f, src_fty);
688+
} else {
689+
coerce_unsized_into(
690+
bcx,
691+
src_f, src_fty,
692+
dst_f, dst_fty
693+
);
694+
}
695+
}
696+
}
697+
_ => bcx.sess().bug(&format!("coerce_unsized_into: invalid coercion {:?} -> {:?}",
698+
src_ty,
699+
dst_ty))
700+
}
701+
}
702+
580703
pub fn cast_shift_expr_rhs(cx: Block,
581704
op: hir::BinOp_,
582705
lhs: ValueRef,

src/librustc_trans/trans/common.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1223,4 +1223,4 @@ pub fn get_static_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
12231223
} else {
12241224
base::get_extern_const(ccx, did, ty)
12251225
}
1226-
}
1226+
}

src/librustc_trans/trans/consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
410410
.expect("consts: unsizing got non-pointer target type").ty;
411411
let ptr_ty = type_of::in_memory_type_of(cx, unsized_ty).ptr_to();
412412
let base = ptrcast(base, ptr_ty);
413-
let info = expr::unsized_info(cx, pointee_ty, unsized_ty,
413+
let info = base::unsized_info(cx, pointee_ty, unsized_ty,
414414
old_info, param_substs);
415415

416416
if old_info.is_none() {

src/librustc_trans/trans/expr.rs

-36
Original file line numberDiff line numberDiff line change
@@ -326,42 +326,6 @@ pub fn copy_fat_ptr(bcx: Block, src_ptr: ValueRef, dst_ptr: ValueRef) {
326326
Store(bcx, Load(bcx, get_meta(bcx, src_ptr)), get_meta(bcx, dst_ptr));
327327
}
328328

329-
/// Retrieve the information we are losing (making dynamic) in an unsizing
330-
/// adjustment.
331-
///
332-
/// The `old_info` argument is a bit funny. It is intended for use
333-
/// in an upcast, where the new vtable for an object will be drived
334-
/// from the old one.
335-
pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>,
336-
source: Ty<'tcx>,
337-
target: Ty<'tcx>,
338-
old_info: Option<ValueRef>,
339-
param_substs: &'tcx Substs<'tcx>)
340-
-> ValueRef {
341-
let (source, target) = ccx.tcx().struct_lockstep_tails(source, target);
342-
match (&source.sty, &target.sty) {
343-
(&ty::TyArray(_, len), &ty::TySlice(_)) => C_uint(ccx, len),
344-
(&ty::TyTrait(_), &ty::TyTrait(_)) => {
345-
// For now, upcasts are limited to changes in marker
346-
// traits, and hence never actually require an actual
347-
// change to the vtable.
348-
old_info.expect("unsized_info: missing old info for trait upcast")
349-
}
350-
(_, &ty::TyTrait(box ty::TraitTy { ref principal, .. })) => {
351-
// Note that we preserve binding levels here:
352-
let substs = principal.0.substs.with_self_ty(source).erase_regions();
353-
let substs = ccx.tcx().mk_substs(substs);
354-
let trait_ref = ty::Binder(ty::TraitRef { def_id: principal.def_id(),
355-
substs: substs });
356-
consts::ptrcast(meth::get_vtable(ccx, trait_ref, param_substs),
357-
Type::vtable_ptr(ccx))
358-
}
359-
_ => ccx.sess().bug(&format!("unsized_info: invalid unsizing {:?} -> {:?}",
360-
source,
361-
target))
362-
}
363-
}
364-
365329
fn adjustment_required<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
366330
expr: &hir::Expr) -> bool {
367331
let adjustment = match bcx.tcx().tables.borrow().adjustments.get(&expr.id).cloned() {

src/librustc_trans/trans/mir/lvalue.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
8585
mir::Lvalue::Arg(index) => self.args[index as usize],
8686
mir::Lvalue::Static(def_id) => {
8787
let const_ty = self.mir.lvalue_ty(tcx, lvalue);
88-
LvalueRef::new(common::get_static_val(ccx, def_id, const_ty.to_ty(tcx)), const_ty)
88+
LvalueRef::new_sized(
89+
common::get_static_val(ccx, def_id, const_ty.to_ty(tcx)),
90+
const_ty)
8991
},
9092
mir::Lvalue::ReturnPointer => {
9193
let return_ty = bcx.monomorphize(&self.mir.return_ty);

src/librustc_trans/trans/mir/rvalue.rs

+78-19
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,35 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
4646
}
4747

4848
mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, cast_ty) => {
49-
let expr_ty =
50-
bcx.monomorphize(&self.mir.operand_ty(bcx.tcx(), operand));
51-
let cast_ty =
52-
bcx.monomorphize(&cast_ty);
53-
if expr_ty == cast_ty {
54-
debug!("trans_rvalue: trivial unsize at {:?}", expr_ty);
55-
self.trans_operand_into(bcx, lldest, operand);
49+
if common::type_is_fat_ptr(bcx.tcx(), cast_ty) {
50+
let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
51+
self.store_operand(bcx, lldest, temp);
5652
return bcx;
5753
}
58-
unimplemented!()
59-
}
6054

61-
mir::Rvalue::Cast(..) => {
62-
unimplemented!()
55+
// Unsize of a nontrivial struct. I would prefer for
56+
// this to be eliminated by MIR translation, but
57+
// `CoerceUnsized` can be passed by a where-clause,
58+
// so the (generic) MIR may not be able to expand it.
59+
let operand = self.trans_operand(bcx, operand);
60+
match operand.val {
61+
OperandValue::FatPtr(..) => unreachable!(),
62+
OperandValue::Imm(llval) => {
63+
// ugly alloca.
64+
debug!("trans_rvalue: creating ugly alloca");
65+
let lltemp = base::alloc_ty(bcx, operand.ty, "__unsize_temp");
66+
base::store_ty(bcx, llval, lltemp, operand.ty);
67+
base::coerce_unsized_into(bcx,
68+
lltemp, operand.ty,
69+
lldest, cast_ty);
70+
}
71+
OperandValue::Ref(llref) => {
72+
base::coerce_unsized_into(bcx,
73+
llref, operand.ty,
74+
lldest, cast_ty);
75+
}
76+
}
77+
bcx
6378
}
6479

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

128-
mir::Rvalue::Cast(mir::CastKind::Unsize, _, _) => {
129-
unimplemented!()
130-
}
143+
mir::Rvalue::Cast(ref kind, ref operand, cast_ty) => {
144+
let operand = self.trans_operand(bcx, operand);
145+
debug!("cast operand is {}", operand.repr(bcx));
146+
let cast_ty = bcx.monomorphize(&cast_ty);
131147

132-
mir::Rvalue::Cast(..) => {
133-
unimplemented!()
148+
let val = match *kind {
149+
mir::CastKind::ReifyFnPointer |
150+
mir::CastKind::UnsafeFnPointer => {
151+
// these are no-ops at the LLVM level
152+
operand.val
153+
}
154+
mir::CastKind::Unsize => {
155+
// unsize targets other than to a fat pointer currently
156+
// can't be operands.
157+
assert!(common::type_is_fat_ptr(bcx.tcx(), cast_ty));
158+
159+
match operand.val {
160+
OperandValue::FatPtr(..) => {
161+
// unsize from a fat pointer - this is a
162+
// "trait-object-to-supertrait" coercion, for
163+
// example,
164+
// &'a fmt::Debug+Send => &'a fmt::Debug,
165+
// and is a no-op at the LLVM level
166+
operand.val
167+
}
168+
OperandValue::Imm(lldata) => {
169+
// "standard" unsize
170+
let (lldata, llextra) =
171+
base::unsize_thin_ptr(bcx, lldata,
172+
operand.ty, cast_ty);
173+
OperandValue::FatPtr(lldata, llextra)
174+
}
175+
OperandValue::Ref(_) => {
176+
bcx.sess().bug(
177+
&format!("by-ref operand {} in trans_rvalue_operand",
178+
operand.repr(bcx)));
179+
}
180+
}
181+
}
182+
mir::CastKind::Misc => unimplemented!()
183+
};
184+
(bcx, OperandRef {
185+
val: val,
186+
ty: cast_ty
187+
})
134188
}
135189

136-
mir::Rvalue::Ref(_, _, ref lvalue) => {
190+
mir::Rvalue::Ref(_, bk, ref lvalue) => {
137191
let tr_lvalue = self.trans_lvalue(bcx, lvalue);
138192

139193
let ty = tr_lvalue.ty.to_ty(bcx.tcx());
194+
let ref_ty = bcx.tcx().mk_ref(
195+
bcx.tcx().mk_region(ty::ReStatic),
196+
ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() }
197+
);
198+
140199
// Note: lvalues are indirect, so storing the `llval` into the
141200
// destination effectively creates a reference.
142201
if common::type_is_sized(bcx.tcx(), ty) {
143202
(bcx, OperandRef {
144203
val: OperandValue::Imm(tr_lvalue.llval),
145-
ty: ty,
204+
ty: ref_ty,
146205
})
147206
} else {
148207
(bcx, OperandRef {
149208
val: OperandValue::FatPtr(tr_lvalue.llval,
150209
tr_lvalue.llextra),
151-
ty: ty,
210+
ty: ref_ty,
152211
})
153212
}
154213
}

0 commit comments

Comments
 (0)