]> git.lizzy.rs Git - rust.git/blobdiff - src/helpers.rs
platform -> target
[rust.git] / src / helpers.rs
index 5128176acb58c66367eaa27ba4c5c773ddb46fb8..36d3181ce4e4b066e9a00bec825fd56b3d684391 100644 (file)
@@ -1,13 +1,14 @@
 use std::ffi::OsStr;
 use std::{iter, mem};
+use std::convert::TryFrom;
 
-use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc::mir;
 use rustc::ty::{
     self,
     layout::{self, LayoutOf, Size, TyLayout},
     List, TyCtxt,
 };
+use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc_span::source_map::DUMMY_SP;
 
 use rand::RngCore;
@@ -17,7 +18,7 @@
 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
 
 /// Gets an instance for a path.
-fn resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> InterpResult<'tcx, DefId> {
+fn try_resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option<DefId> {
     tcx.crates()
         .iter()
         .find(|&&krate| tcx.original_crate_name(krate).as_str() == path[0])
@@ -40,19 +41,47 @@ fn resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> InterpResult<'tc
             }
             None
         })
-        .ok_or_else(|| {
-            let path = path.iter().map(|&s| s.to_owned()).collect();
-            err_unsup!(PathNotFound(path)).into()
-        })
 }
 
 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
     /// Gets an instance for a path.
-    fn resolve_path(&self, path: &[&str]) -> InterpResult<'tcx, ty::Instance<'tcx>> {
-        Ok(ty::Instance::mono(
-            self.eval_context_ref().tcx.tcx,
-            resolve_did(self.eval_context_ref().tcx.tcx, path)?,
-        ))
+    fn resolve_path(&self, path: &[&str]) -> ty::Instance<'tcx> {
+        let did = try_resolve_did(self.eval_context_ref().tcx.tcx, path)
+            .unwrap_or_else(|| panic!("failed to find required Rust item: {:?}", path));
+        ty::Instance::mono(self.eval_context_ref().tcx.tcx, did)
+    }
+
+    /// Evaluates the scalar at the specified path. Returns Some(val)
+    /// if the path could be resolved, and None otherwise
+    fn eval_path_scalar(
+        &mut self,
+        path: &[&str],
+    ) -> InterpResult<'tcx, ScalarMaybeUndef<Tag>> {
+        let this = self.eval_context_mut();
+        let instance = this.resolve_path(path);
+        let cid = GlobalId { instance, promoted: None };
+        let const_val = this.const_eval_raw(cid)?;
+        let const_val = this.read_scalar(const_val.into())?;
+        return Ok(const_val);
+    }
+
+    /// Helper function to get a `libc` constant as a `Scalar`.
+    fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar<Tag>> {
+        self.eval_context_mut()
+            .eval_path_scalar(&["libc", name])?
+            .not_undef()
+    }
+
+    /// Helper function to get a `libc` constant as an `i32`.
+    fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> {
+        self.eval_libc(name)?.to_i32()
+    }
+
+    /// Helper function to get the `TyLayout` of a `libc` type
+    fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyLayout<'tcx>> {
+        let this = self.eval_context_mut();
+        let ty = this.resolve_path(&["libc", name]).monomorphic_ty(*this.tcx);
+        this.layout_of(ty)
     }
 
     /// Write a 0 of the appropriate size to `dest`.
@@ -81,7 +110,7 @@ fn local_place(&mut self, local: mir::Local) -> InterpResult<'tcx, PlaceTy<'tcx,
     }
 
     /// Generate some random bytes, and write them to `dest`.
-    fn gen_random(&mut self, ptr: Scalar<Tag>, len: usize) -> InterpResult<'tcx> {
+    fn gen_random(&mut self, ptr: Scalar<Tag>, len: u64) -> InterpResult<'tcx> {
         // Some programs pass in a null pointer and a length of 0
         // to their platform's random-generation function (e.g. getrandom())
         // on Linux. For compatibility with these programs, we don't perform
@@ -92,12 +121,12 @@ fn gen_random(&mut self, ptr: Scalar<Tag>, len: usize) -> InterpResult<'tcx> {
         }
         let this = self.eval_context_mut();
 
-        let mut data = vec![0; len];
+        let mut data = vec![0; usize::try_from(len).unwrap()];
 
         if this.machine.communicate {
             // Fill the buffer using the host's rng.
             getrandom::getrandom(&mut data)
-                .map_err(|err| err_unsup_format!("getrandom failed: {}", err))?;
+                .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
         } else {
             let rng = this.memory.extra.rng.get_mut();
             rng.fill_bytes(&mut data);
@@ -300,41 +329,18 @@ fn visit_aggregate(
             }
 
             // We have to do *something* for unions.
-            fn visit_union(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
+            fn visit_union(&mut self, v: MPlaceTy<'tcx, Tag>, fields: usize) -> InterpResult<'tcx> {
+                assert!(fields > 0); // we should never reach "pseudo-unions" with 0 fields, like primitives
+
                 // With unions, we fall back to whatever the type says, to hopefully be consistent
                 // with LLVM IR.
                 // FIXME: are we consistent, and is this really the behavior we want?
                 let frozen = self.ecx.type_is_freeze(v.layout.ty);
                 if frozen { Ok(()) } else { (self.unsafe_cell_action)(v) }
             }
-
-            // We should never get to a primitive, but always short-circuit somewhere above.
-            fn visit_primitive(&mut self, _v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
-                bug!("we should always short-circuit before coming to a primitive")
-            }
         }
     }
 
-    /// Helper function to get a `libc` constant as a `Scalar`.
-    fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar<Tag>> {
-        self.eval_context_mut()
-            .eval_path_scalar(&["libc", name])?
-            .ok_or_else(|| err_unsup_format!("Path libc::{} cannot be resolved.", name))?
-            .not_undef()
-    }
-
-    /// Helper function to get a `libc` constant as an `i32`.
-    fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> {
-        self.eval_libc(name)?.to_i32()
-    }
-
-    /// Helper function to get the `TyLayout` of a `libc` type
-    fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyLayout<'tcx>> {
-        let this = self.eval_context_mut();
-        let ty = this.resolve_path(&["libc", name])?.monomorphic_ty(*this.tcx);
-        this.layout_of(ty)
-    }
-
     // Writes several `ImmTy`s contiguosly into memory. This is useful when you have to pack
     // different values into a struct.
     fn write_packed_immediates(
@@ -362,22 +368,22 @@ fn write_packed_immediates(
     fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
         if !self.eval_context_ref().machine.communicate {
             throw_unsup_format!(
-                "`{}` not available when isolation is enabled. Pass the flag `-Zmiri-disable-isolation` to disable it.",
+                "`{}` not available when isolation is enabled (pass the flag `-Zmiri-disable-isolation` to disable isolation)",
                 name,
             )
         }
         Ok(())
     }
-    /// Helper function used inside the shims of foreign functions to assert that the target
-    /// platform is `platform`. It panics showing a message with the `name` of the foreign function
+    /// Helper function used inside the shims of foreign functions to assert that the target OS
+    /// is `target_os`. It panics showing a message with the `name` of the foreign function
     /// if this is not the case.
-    fn assert_platform(&self, platform: &str, name: &str) {
+    fn assert_target_os(&self, target_os: &str, name: &str) {
         assert_eq!(
             self.eval_context_ref().tcx.sess.target.target.target_os,
-            platform,
-            "`{}` is only available on the `{}` platform",
+            target_os,
+            "`{}` is only available on the `{}` target OS",
             name,
-            platform,
+            target_os,
         )
     }
 
@@ -418,13 +424,13 @@ fn set_last_error_from_io_error(&mut self, e: std::io::Error) -> InterpResult<'t
                 AlreadyExists => "EEXIST",
                 WouldBlock => "EWOULDBLOCK",
                 _ => {
-                    throw_unsup_format!("The {} error cannot be transformed into a raw os error", e)
+                    throw_unsup_format!("io error {} cannot be transformed into a raw os error", e)
                 }
             })?
         } else {
             // FIXME: we have to implement the Windows equivalent of this.
             throw_unsup_format!(
-                "Setting the last OS error from an io::Error is unsupported for {}.",
+                "setting the last OS error from an io::Error is unsupported for {}.",
                 target.target_os
             )
         };
@@ -474,15 +480,16 @@ fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> {
     }
 
     /// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
-    /// the Unix APIs usually handle. This function returns `Ok(false)` without trying to write if
-    /// `size` is not large enough to fit the contents of `os_string` plus a null terminator. It
-    /// returns `Ok(true)` if the writing process was successful.
+    /// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
+    /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
+    /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
+    /// string length returned does not include the null terminator.
     fn write_os_str_to_c_str(
         &mut self,
         os_str: &OsStr,
         scalar: Scalar<Tag>,
         size: u64,
-    ) -> InterpResult<'tcx, bool> {
+    ) -> InterpResult<'tcx, (bool, u64)> {
         #[cfg(target_os = "unix")]
         fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
             std::os::unix::ffi::OsStringExt::into_bytes(os_str)
@@ -501,21 +508,22 @@ fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]>
         let bytes = os_str_to_bytes(os_str)?;
         // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
         // terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
-        if size <= bytes.len() as u64 {
-            return Ok(false);
+        let string_length = u64::try_from(bytes.len()).unwrap();
+        if size <= string_length {
+            return Ok((false, string_length));
         }
         self.eval_context_mut()
             .memory
             .write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?;
-        Ok(true)
+        Ok((true, string_length))
     }
 
     fn alloc_os_str_as_c_str(
         &mut self,
         os_str: &OsStr,
-        memkind: MemoryKind<MiriMemoryKind>
+        memkind: MemoryKind<MiriMemoryKind>,
     ) -> Pointer<Tag> {
-        let size = os_str.len() as u64 + 1; // Make space for `0` terminator.
+        let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator.
         let this = self.eval_context_mut();
 
         let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
@@ -530,9 +538,9 @@ pub fn immty_from_int_checked<'tcx>(
     layout: TyLayout<'tcx>,
 ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> {
     let int = int.into();
-    Ok(ImmTy::try_from_int(int, layout).ok_or_else(||
-        err_unsup_format!("Signed value {:#x} does not fit in {} bits", int, layout.size.bits())
-    )?)
+    Ok(ImmTy::try_from_int(int, layout).ok_or_else(|| {
+        err_unsup_format!("signed value {:#x} does not fit in {} bits", int, layout.size.bits())
+    })?)
 }
 
 pub fn immty_from_uint_checked<'tcx>(
@@ -540,7 +548,7 @@ pub fn immty_from_uint_checked<'tcx>(
     layout: TyLayout<'tcx>,
 ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> {
     let int = int.into();
-    Ok(ImmTy::try_from_uint(int, layout).ok_or_else(||
-        err_unsup_format!("Signed value {:#x} does not fit in {} bits", int, layout.size.bits())
-    )?)
+    Ok(ImmTy::try_from_uint(int, layout).ok_or_else(|| {
+        err_unsup_format!("unsigned value {:#x} does not fit in {} bits", int, layout.size.bits())
+    })?)
 }