]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/interpret/intrinsics.rs
Return Ok(false) instead of throwing when handling a diverging intrinsic
[rust.git] / src / librustc_mir / interpret / intrinsics.rs
index 519f4f0322228e9474d7c45f674eaec7e09c7496..66b6d4ac12c402c38381c942a3d39df43ef52f30 100644 (file)
@@ -13,7 +13,7 @@
 use rustc::mir::interpret::{InterpResult, Scalar, GlobalId, ConstValue};
 
 use super::{
-    Machine, PlaceTy, OpTy, InterpCx,
+    Machine, PlaceTy, OpTy, InterpCx, ImmTy,
 };
 
 mod caller_location;
@@ -91,14 +91,21 @@ pub fn emulate_intrinsic(
         span: Span,
         instance: ty::Instance<'tcx>,
         args: &[OpTy<'tcx, M::PointerTag>],
-        dest: PlaceTy<'tcx, M::PointerTag>,
+        dest: Option<PlaceTy<'tcx, M::PointerTag>>,
     ) -> InterpResult<'tcx, bool> {
         let substs = instance.substs;
 
-        let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..];
+        // We currently do not handle any diverging intrinsics.
+        let dest = match dest {
+            Some(dest) => dest,
+            None => return Ok(false)
+        };
+        let intrinsic_name = &*self.tcx.item_name(instance.def_id()).as_str();
+
         match intrinsic_name {
             "caller_location" => {
-                let caller = self.tcx.sess.source_map().lookup_char_pos(span.lo());
+                let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
+                let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
                 let location = self.alloc_caller_location(
                     Symbol::intern(&caller.file.name.to_string()),
                     caller.line as u32,
@@ -249,6 +256,48 @@ pub fn emulate_intrinsic(
                 let result = Scalar::from_uint(truncated_bits, layout.size);
                 self.write_scalar(result, dest)?;
             }
+
+            "ptr_offset_from" => {
+                let isize_layout = self.layout_of(self.tcx.types.isize)?;
+                let a = self.read_immediate(args[0])?.to_scalar()?;
+                let b = self.read_immediate(args[1])?.to_scalar()?;
+
+                // Special case: if both scalars are *equal integers*
+                // and not NULL, we pretend there is an allocation of size 0 right there,
+                // and their offset is 0. (There's never a valid object at NULL, making it an
+                // exception from the exception.)
+                // This is the dual to the special exception for offset-by-0
+                // in the inbounds pointer offset operation (see the Miri code, `src/operator.rs`).
+                if a.is_bits() && b.is_bits() {
+                    let a = a.to_machine_usize(self)?;
+                    let b = b.to_machine_usize(self)?;
+                    if a == b && a != 0 {
+                        self.write_scalar(Scalar::from_int(0, isize_layout.size), dest)?;
+                        return Ok(true);
+                    }
+                }
+
+                // General case: we need two pointers.
+                let a = self.force_ptr(a)?;
+                let b = self.force_ptr(b)?;
+                if a.alloc_id != b.alloc_id {
+                    throw_ub_format!(
+                        "ptr_offset_from cannot compute offset of pointers into different \
+                        allocations.",
+                    );
+                }
+                let usize_layout = self.layout_of(self.tcx.types.usize)?;
+                let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout);
+                let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout);
+                let (val, _overflowed, _ty) = self.overflowing_binary_op(
+                    BinOp::Sub, a_offset, b_offset,
+                )?;
+                let pointee_layout = self.layout_of(substs.type_at(0))?;
+                let val = ImmTy::from_scalar(val, isize_layout);
+                let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
+                self.exact_div(val, size, dest)?;
+            }
+
             "transmute" => {
                 self.copy_op_transmute(args[0], dest)?;
             }
@@ -304,9 +353,10 @@ pub fn emulate_intrinsic(
         Ok(true)
     }
 
-    /// "Intercept" a function call because we have something special to do for it.
+    /// "Intercept" a function call to a panic-related function
+    /// because we have something special to do for it.
     /// Returns `true` if an intercept happened.
-    pub fn hook_fn(
+    pub fn hook_panic_fn(
         &mut self,
         instance: ty::Instance<'tcx>,
         args: &[OpTy<'tcx, M::PointerTag>],
@@ -354,4 +404,30 @@ pub fn hook_fn(
             return Ok(false);
         }
     }
+
+    pub fn exact_div(
+        &mut self,
+        a: ImmTy<'tcx, M::PointerTag>,
+        b: ImmTy<'tcx, M::PointerTag>,
+        dest: PlaceTy<'tcx, M::PointerTag>,
+    ) -> InterpResult<'tcx> {
+        // Performs an exact division, resulting in undefined behavior where
+        // `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1`.
+        // First, check x % y != 0.
+        if self.binary_op(BinOp::Rem, a, b)?.to_bits()? != 0 {
+            // Then, check if `b` is -1, which is the "min_value / -1" case.
+            let minus1 = Scalar::from_int(-1, dest.layout.size);
+            let b = b.to_scalar().unwrap();
+            if b == minus1 {
+                throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented")
+            } else {
+                throw_ub_format!(
+                    "exact_div: {} cannot be divided by {} without remainder",
+                    a.to_scalar().unwrap(),
+                    b,
+                )
+            }
+        }
+        self.binop_ignore_overflow(BinOp::Div, a, b, dest)
+    }
 }