use rustc::ty::{self, Ty, TypeAndMut};
use rustc::ty::layout::{self, TyLayout, Size};
use rustc::ty::adjustment::{PointerCast};
-use syntax::ast::{FloatTy, IntTy, UintTy};
+use syntax::ast::FloatTy;
use syntax::symbol::sym;
use rustc_apfloat::ieee::{Single, Double};
use rustc_apfloat::{Float, FloatConvert};
use rustc::mir::interpret::{
- Scalar, InterpResult, Pointer, PointerArithmetic, InterpError,
+ Scalar, InterpResult, PointerArithmetic,
};
use rustc::mir::CastKind;
-use super::{InterpretCx, Machine, PlaceTy, OpTy, Immediate};
-
-impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
- fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
- match ty.sty {
- ty::RawPtr(ty::TypeAndMut { ty, .. }) |
- ty::Ref(_, ty, _) => !self.type_is_sized(ty),
- ty::Adt(def, _) if def.is_box() => !self.type_is_sized(ty.boxed_ty()),
- _ => false,
- }
- }
+use super::{InterpCx, Machine, PlaceTy, OpTy, ImmTy, Immediate, FnVal};
+impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub fn cast(
&mut self,
src: OpTy<'tcx, M::PointerTag>,
Misc | Pointer(PointerCast::MutToConstPointer) => {
let src = self.read_immediate(src)?;
-
- if self.type_is_fat_ptr(src.layout.ty) {
- match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
- // pointers to extern types
- (Immediate::Scalar(_),_) |
- // slices and trait objects to other slices/trait objects
- (Immediate::ScalarPair(..), true) => {
- // No change to immediate
- self.write_immediate(*src, dest)?;
- }
- // slices and trait objects to thin pointers (dropping the metadata)
- (Immediate::ScalarPair(data, _), false) => {
- self.write_scalar(data, dest)?;
- }
- }
- } else {
- match src.layout.variants {
- layout::Variants::Single { index } => {
- if let Some(discr) =
- src.layout.ty.discriminant_for_variant(*self.tcx, index)
- {
- // Cast from a univariant enum
- assert!(src.layout.is_zst());
- return self.write_scalar(
- Scalar::from_uint(discr.val, dest.layout.size),
- dest);
- }
- }
- layout::Variants::Multiple { .. } => {},
- }
-
- let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?;
- self.write_scalar(dest_val, dest)?;
- }
+ let res = self.cast_immediate(src, dest.layout)?;
+ self.write_immediate(res, dest)?;
}
Pointer(PointerCast::ReifyFnPointer) => {
if self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
bug!("reifying a fn ptr that requires const arguments");
}
- let instance: InterpResult<'tcx, _> = ty::Instance::resolve(
+ let instance = ty::Instance::resolve(
*self.tcx,
self.param_env,
def_id,
substs,
- ).ok_or_else(|| InterpError::TooGeneric.into());
- let fn_ptr = self.memory.create_fn_alloc(instance?);
+ ).ok_or_else(|| err_inval!(TooGeneric))?;
+ let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
}
_ => bug!("reify fn pointer on {:?}", src.layout.ty),
substs,
ty::ClosureKind::FnOnce,
);
- let fn_ptr = self.memory.create_fn_alloc(instance);
+ let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
let val = Immediate::Scalar(Scalar::Ptr(fn_ptr.into()).into());
self.write_immediate(val, dest)?;
}
Ok(())
}
- fn cast_scalar(
+ fn cast_immediate(
&self,
- val: Scalar<M::PointerTag>,
- src_layout: TyLayout<'tcx>,
+ src: ImmTy<'tcx, M::PointerTag>,
dest_layout: TyLayout<'tcx>,
- ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
+ ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
use rustc::ty::TyKind::*;
- trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty);
+ trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, dest_layout.ty);
- match src_layout.ty.sty {
+ match src.layout.ty.sty {
// Floating point
- Float(FloatTy::F32) => self.cast_from_float(val.to_f32()?, dest_layout.ty),
- Float(FloatTy::F64) => self.cast_from_float(val.to_f64()?, dest_layout.ty),
- // Integer(-like), including fn ptr casts and casts from enums that
- // are represented as integers (this excludes univariant enums, which
- // are handled in `cast` directly).
- _ => {
+ Float(FloatTy::F32) =>
+ return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, dest_layout.ty)?.into()),
+ Float(FloatTy::F64) =>
+ return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, dest_layout.ty)?.into()),
+ // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that
+ // are represented as integers.
+ _ =>
assert!(
- src_layout.ty.is_bool() || src_layout.ty.is_char() ||
- src_layout.ty.is_enum() || src_layout.ty.is_integral() ||
- src_layout.ty.is_unsafe_ptr() || src_layout.ty.is_fn_ptr() ||
- src_layout.ty.is_region_ptr(),
- "Unexpected cast from type {:?}", src_layout.ty
- );
- match val.to_bits_or_ptr(src_layout.size, self) {
- Err(ptr) => self.cast_from_ptr(ptr, dest_layout.ty),
- Ok(data) => self.cast_from_int(data, src_layout, dest_layout),
+ src.layout.ty.is_bool() || src.layout.ty.is_char() ||
+ src.layout.ty.is_enum() || src.layout.ty.is_integral() ||
+ src.layout.ty.is_any_ptr(),
+ "Unexpected cast from type {:?}", src.layout.ty
+ )
+ }
+
+ // Handle cast from a univariant (ZST) enum.
+ match src.layout.variants {
+ layout::Variants::Single { index } => {
+ if let Some(discr) =
+ src.layout.ty.discriminant_for_variant(*self.tcx, index)
+ {
+ assert!(src.layout.is_zst());
+ return Ok(Scalar::from_uint(discr.val, dest_layout.size).into());
}
}
+ layout::Variants::Multiple { .. } => {},
+ }
+
+ // Handle casting the metadata away from a fat pointer.
+ if src.layout.ty.is_unsafe_ptr() && dest_layout.ty.is_unsafe_ptr() &&
+ dest_layout.size != src.layout.size
+ {
+ assert_eq!(src.layout.size, 2*self.memory.pointer_size());
+ assert_eq!(dest_layout.size, self.memory.pointer_size());
+ assert!(dest_layout.ty.is_unsafe_ptr());
+ match *src {
+ Immediate::ScalarPair(data, _) =>
+ return Ok(data.into()),
+ Immediate::Scalar(..) =>
+ bug!(
+ "{:?} input to a fat-to-thin cast ({:?} -> {:?})",
+ *src, src.layout.ty, dest_layout.ty
+ ),
+ };
+ }
+
+ // Handle casting any ptr to raw ptr (might be a fat ptr).
+ if src.layout.ty.is_any_ptr() && dest_layout.ty.is_unsafe_ptr()
+ {
+ // The only possible size-unequal case was handled above.
+ assert_eq!(src.layout.size, dest_layout.size);
+ return Ok(*src);
}
+
+ // For all remaining casts, we either
+ // (a) cast a raw ptr to usize, or
+ // (b) cast from an integer-like (including bool, char, enums).
+ // In both cases we want the bits.
+ let bits = self.force_bits(src.to_scalar()?, src.layout.size)?;
+ Ok(self.cast_from_int(bits, src.layout, dest_layout)?.into())
}
fn cast_from_int(
},
// Casts to bool are not permitted by rustc, no need to handle them here.
- _ => err!(Unimplemented(format!("int to {:?} cast", dest_layout.ty))),
+ _ => bug!("invalid int to {:?} cast", dest_layout.ty),
}
}
}
}
- fn cast_from_ptr(
- &self,
- ptr: Pointer<M::PointerTag>,
- ty: Ty<'tcx>
- ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
- use rustc::ty::TyKind::*;
- match ty.sty {
- // Casting to a reference or fn pointer is not permitted by rustc,
- // no need to support it here.
- RawPtr(_) |
- Int(IntTy::Isize) |
- Uint(UintTy::Usize) => Ok(ptr.into()),
- Int(_) | Uint(_) => err!(ReadPointerAsBytes),
- _ => err!(Unimplemented(format!("ptr to {:?} cast", ty))),
- }
- }
-
fn unsize_into_ptr(
&mut self,
src: OpTy<'tcx, M::PointerTag>,
dty: Ty<'tcx>,
) -> InterpResult<'tcx> {
// A<Struct> -> A<Trait> conversion
- let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(sty, dty);
+ let (src_pointee_ty, dest_pointee_ty) =
+ self.tcx.struct_lockstep_tails_erasing_lifetimes(sty, dty, self.param_env);
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
(&ty::Array(_, length), &ty::Slice(_)) => {
// u64 cast is from usize to u64, which is always good
let val = Immediate::new_slice(
ptr,
- length.unwrap_usize(self.tcx.tcx),
+ length.eval_usize(self.tcx.tcx, self.param_env),
self,
);
self.write_immediate(val, dest)