]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_mir/interpret/cast.rs
New upstream version 1.38.0+dfsg1
[rustc.git] / src / librustc_mir / interpret / cast.rs
index fbacdf6cd93bbf80629c9206df2a9db05788a85b..26cfbfe53a35cdf856320174e6dd7012bb403de9 100644 (file)
@@ -1,28 +1,19 @@
 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>,
@@ -37,40 +28,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
 
             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) => {
@@ -80,13 +39,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
                         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),
@@ -115,7 +74,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
                             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)?;
                     }
@@ -126,36 +85,76 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
         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(
@@ -199,7 +198,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
             },
 
             // 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),
         }
     }
 
@@ -236,23 +235,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
         }
     }
 
-    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>,
@@ -262,7 +244,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
         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(_)) => {
@@ -270,7 +253,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
                 // 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)