X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fhelpers.rs;h=4c989db0170b5408c71116555a8fcfc0702aab17;hb=296ba8b1c84f65d123fa5b35f6f2e47e73710b08;hp=d8731b537719b9ce229da2344f10ce669111a214;hpb=4ac91384ff124d5b6258a648df5176909ff1b780;p=rust.git diff --git a/src/helpers.rs b/src/helpers.rs index d8731b53771..4c989db0170 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,23 +1,20 @@ -use std::ffi::{OsStr, OsString}; -use std::path::Path; -use std::{iter, mem}; -use std::convert::TryFrom; -use std::borrow::Cow; - -use rustc::mir; -use rustc::ty::{ - self, - layout::{self, LayoutOf, Size, TyLayout}, - List, TyCtxt, -}; +use std::convert::{TryFrom, TryInto}; +use std::mem; +use std::num::NonZeroUsize; +use std::time::Duration; + +use log::trace; + +use rustc_middle::mir; +use rustc_middle::ty::{self, List, TyCtxt, layout::TyAndLayout}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; -use rustc_span::source_map::DUMMY_SP; +use rustc_target::abi::{LayoutOf, Size, FieldsShape, Variants}; use rand::RngCore; use crate::*; -impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} /// Gets an instance for a path. fn try_resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option { @@ -58,11 +55,11 @@ fn resolve_path(&self, path: &[&str]) -> ty::Instance<'tcx> { fn eval_path_scalar( &mut self, path: &[&str], - ) -> InterpResult<'tcx, ScalarMaybeUndef> { + ) -> InterpResult<'tcx, ScalarMaybeUninit> { 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.eval_to_allocation(cid)?; let const_val = this.read_scalar(const_val.into())?; return Ok(const_val); } @@ -71,18 +68,39 @@ fn eval_path_scalar( fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar> { self.eval_context_mut() .eval_path_scalar(&["libc", name])? - .not_undef() + .check_init() } /// Helper function to get a `libc` constant as an `i32`. fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> { + // TODO: Cache the result. 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>> { + /// Helper function to get a `windows` constant as a `Scalar`. + fn eval_windows(&mut self, module: &str, name: &str) -> InterpResult<'tcx, Scalar> { + self.eval_context_mut() + .eval_path_scalar(&["std", "sys", "windows", module, name])? + .check_init() + } + + /// Helper function to get a `windows` constant as an `u64`. + fn eval_windows_u64(&mut self, module: &str, name: &str) -> InterpResult<'tcx, u64> { + // TODO: Cache the result. + self.eval_windows(module, name)?.to_u64() + } + + /// Helper function to get the `TyAndLayout` of a `libc` type + fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> { let this = self.eval_context_mut(); - let ty = this.resolve_path(&["libc", name]).monomorphic_ty(*this.tcx); + let ty = this.resolve_path(&["libc", name]).ty(*this.tcx, ty::ParamEnv::reveal_all()); + this.layout_of(ty) + } + + /// Helper function to get the `TyAndLayout` of a `windows` type + fn windows_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> { + let this = self.eval_context_mut(); + let ty = this.resolve_path(&["std", "sys", "windows", "c", name]).ty(*this.tcx, ty::ParamEnv::reveal_all()); this.layout_of(ty) } @@ -94,7 +112,7 @@ fn write_null(&mut self, dest: PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { /// Test if this immediate equals 0. fn is_null(&self, val: Scalar) -> InterpResult<'tcx, bool> { let this = self.eval_context_ref(); - let null = Scalar::from_int(0, this.memory.pointer_size()); + let null = Scalar::null_ptr(this); this.ptr_eq(val, null) } @@ -108,7 +126,7 @@ fn test_null(&self, val: Scalar) -> InterpResult<'tcx, Option>> fn local_place(&mut self, local: mir::Local) -> InterpResult<'tcx, PlaceTy<'tcx, Tag>> { let this = self.eval_context_mut(); let place = mir::Place { local: local, projection: List::empty() }; - this.eval_place(&place) + this.eval_place(place) } /// Generate some random bytes, and write them to `dest`. @@ -150,13 +168,7 @@ fn call_function( // Push frame. let mir = &*this.load_mir(f.def, None)?; - let span = this - .stack() - .last() - .and_then(Frame::current_source_info) - .map(|si| si.span) - .unwrap_or(DUMMY_SP); - this.push_stack_frame(f, span, mir, dest, stack_pop)?; + this.push_stack_frame(f, mir, dest, stack_pop)?; // Initialize arguments. let mut callee_args = this.frame().body.args_iter(); @@ -254,7 +266,7 @@ struct UnsafeCellVisitor<'ecx, 'mir, 'tcx, F> unsafe_cell_action: F, } - impl<'ecx, 'mir, 'tcx, F> ValueVisitor<'mir, 'tcx, Evaluator<'tcx>> + impl<'ecx, 'mir, 'tcx: 'mir, F> ValueVisitor<'mir, 'tcx, Evaluator<'mir, 'tcx>> for UnsafeCellVisitor<'ecx, 'mir, 'tcx, F> where F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>, @@ -269,7 +281,7 @@ fn ecx(&self) -> &MiriEvalContext<'mir, 'tcx> { // Hook to detect `UnsafeCell`. fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty); - let is_unsafe_cell = match v.layout.ty.kind { + let is_unsafe_cell = match v.layout.ty.kind() { ty::Adt(adt, _) => Some(adt.did) == self.ecx.tcx.lang_items().unsafe_cell_type(), _ => false, @@ -280,12 +292,15 @@ fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { } else if self.ecx.type_is_freeze(v.layout.ty) { // This is `Freeze`, there cannot be an `UnsafeCell` Ok(()) + } else if matches!(v.layout.fields, FieldsShape::Union(..)) { + // A (non-frozen) union. We fall back to whatever the type says. + (self.unsafe_cell_action)(v) } else { // We want to not actually read from memory for this visit. So, before // walking this value, we have to make sure it is not a // `Variants::Multiple`. match v.layout.variants { - layout::Variants::Multiple { .. } => { + Variants::Multiple { .. } => { // A multi-variant enum, or generator, or so. // Treat this like a union: without reading from memory, // we cannot determine the variant we are in. Reading from @@ -295,7 +310,7 @@ fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { // `UnsafeCell` action. (self.unsafe_cell_action)(v) } - layout::Variants::Single { .. } => { + Variants::Single { .. } => { // Proceed further, try to find where exactly that `UnsafeCell` // is hiding. self.walk_value(v) @@ -311,39 +326,32 @@ fn visit_aggregate( fields: impl Iterator>>, ) -> InterpResult<'tcx> { match place.layout.fields { - layout::FieldPlacement::Array { .. } => { + FieldsShape::Array { .. } => { // For the array layout, we know the iterator will yield sorted elements so // we can avoid the allocation. self.walk_aggregate(place, fields) } - layout::FieldPlacement::Arbitrary { .. } => { + FieldsShape::Arbitrary { .. } => { // Gather the subplaces and sort them before visiting. let mut places = fields.collect::>>>()?; places.sort_by_key(|place| place.ptr.assert_ptr().offset); self.walk_aggregate(place, places.into_iter().map(Ok)) } - layout::FieldPlacement::Union { .. } => { + FieldsShape::Union { .. } | FieldsShape::Primitive => { // Uh, what? - bug!("a union is not an aggregate we should ever visit") + bug!("unions/primitives are not aggregates we should ever visit") } } } - // We have to do *something* for unions. - 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) } + fn visit_union(&mut self, _v: MPlaceTy<'tcx, Tag>, _fields: NonZeroUsize) -> InterpResult<'tcx> { + bug!("we should have already handled unions in `visit_value`") } } } - // Writes several `ImmTy`s contiguosly into memory. This is useful when you have to pack + // Writes several `ImmTy`s contiguously into memory. This is useful when you have to pack // different values into a struct. fn write_packed_immediates( &mut self, @@ -369,19 +377,17 @@ fn write_packed_immediates( /// case. fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> { if !self.eval_context_ref().machine.communicate { - throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!( - "`{}` not available when isolation is enabled", - name, - ))) + isolation_error(name)?; } Ok(()) } + /// 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_target_os(&self, target_os: &str, name: &str) { assert_eq!( - self.eval_context_ref().tcx.sess.target.target.target_os, + self.eval_context_ref().tcx.sess.target.os, target_os, "`{}` is only available on the `{}` target OS", name, @@ -389,18 +395,34 @@ fn assert_target_os(&self, target_os: &str, name: &str) { ) } + /// Get last error variable as a place, lazily allocating thread-local storage for it if + /// necessary. + fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> { + let this = self.eval_context_mut(); + if let Some(errno_place) = this.active_thread_ref().last_error { + Ok(errno_place) + } else { + // Allocate new place, set initial value to 0. + let errno_layout = this.machine.layouts.u32; + let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into()); + this.write_scalar(Scalar::from_u32(0), errno_place.into())?; + this.active_thread_mut().last_error = Some(errno_place); + Ok(errno_place) + } + } + /// Sets the last error variable. fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let errno_place = this.machine.last_error.unwrap(); + let errno_place = this.last_error_place()?; this.write_scalar(scalar, errno_place.into()) } /// Gets the last error variable. - fn get_last_error(&self) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_ref(); - let errno_place = this.machine.last_error.unwrap(); - this.read_scalar(errno_place.into())?.not_undef() + fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + let errno_place = this.last_error_place()?; + this.read_scalar(errno_place.into())?.check_init() } /// Sets the last OS error using a `std::io::Error`. This function tries to produce the most @@ -408,8 +430,9 @@ fn get_last_error(&self) -> InterpResult<'tcx, Scalar> { fn set_last_error_from_io_error(&mut self, e: std::io::Error) -> InterpResult<'tcx> { use std::io::ErrorKind::*; let this = self.eval_context_mut(); - let target = &this.tcx.tcx.sess.target.target; - let last_error = if target.options.target_family == Some("unix".to_owned()) { + let target = &this.tcx.sess.target; + let target_os = &target.os; + let last_error = if target.os_family == Some("unix".to_owned()) { this.eval_libc(match e.kind() { ConnectionRefused => "ECONNREFUSED", ConnectionReset => "ECONNRESET", @@ -429,12 +452,14 @@ fn set_last_error_from_io_error(&mut self, e: std::io::Error) -> InterpResult<'t throw_unsup_format!("io error {} cannot be transformed into a raw os error", e) } })? + } else if target_os == "windows" { + // FIXME: we have to finish implementing the Windows equivalent of this. + this.eval_windows("c", match e.kind() { + NotFound => "ERROR_FILE_NOT_FOUND", + _ => 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 {}.", - target.target_os - ) + throw_unsup_format!("setting the last OS error from an io::Error is unsupported for {}.", target_os) }; this.set_last_error(last_error) } @@ -458,215 +483,86 @@ fn try_unwrap_io_result>( } } - /// Dispatches to appropriate implementations for reading an OsString from Memory, - /// depending on the interpretation target. - /// FIXME: Use `Cow` to avoid copies - fn read_os_str_from_target_str(&self, scalar: Scalar) -> InterpResult<'tcx, OsString> { - let target_os = self.eval_context_ref().tcx.sess.target.target.target_os.as_str(); - match target_os { - "linux" | "macos" => self.read_os_str_from_c_str(scalar).map(|x| x.to_os_string()), - "windows" => self.read_os_str_from_wide_str(scalar), - unsupported => throw_unsup_format!("OsString support for target OS `{}` not yet available", unsupported), - } - } - - /// Helper function to read an OsString from a null-terminated sequence of bytes, which is what - /// the Unix APIs usually handle. - fn read_os_str_from_c_str<'a>(&'a self, scalar: Scalar) -> InterpResult<'tcx, &'a OsStr> - where - 'tcx: 'a, - 'mir: 'a, - { - #[cfg(unix)] - fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> { - Ok(std::os::unix::ffi::OsStrExt::from_bytes(bytes)) - } - #[cfg(not(unix))] - fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> { - let s = std::str::from_utf8(bytes) - .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?; - Ok(OsStr::new(s)) - } - + fn read_scalar_at_offset( + &self, + op: OpTy<'tcx, Tag>, + offset: u64, + layout: TyAndLayout<'tcx>, + ) -> InterpResult<'tcx, ScalarMaybeUninit> { let this = self.eval_context_ref(); - let bytes = this.memory.read_c_str(scalar)?; - bytes_to_os_str(bytes) - } - - /// Read a null-terminated sequence of bytes, and perform path separator conversion if needed. - fn read_path_from_c_str<'a>(&'a self, scalar: Scalar) -> InterpResult<'tcx, Cow<'a, Path>> - where - 'tcx: 'a, - 'mir: 'a, - { - let os_str = self.read_os_str_from_c_str(scalar)?; - Ok(Cow::Borrowed(Path::new(os_str))) + let op_place = this.deref_operand(op)?; + let offset = Size::from_bytes(offset); + // Ensure that the following read at an offset is within bounds + assert!(op_place.layout.size >= offset + layout.size); + let value_place = op_place.offset(offset, MemPlaceMeta::None, layout, this)?; + this.read_scalar(value_place.into()) } - /// Helper function to read an OsString from a 0x0000-terminated sequence of u16, - /// which is what the Windows APIs usually handle. - fn read_os_str_from_wide_str<'a>(&'a self, scalar: Scalar) -> InterpResult<'tcx, OsString> - where - 'tcx: 'a, - 'mir: 'a, - { - #[cfg(windows)] - pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec) -> InterpResult<'tcx, OsString> { - Ok(std::os::windows::ffi::OsStringExt::from_wide(&u16_vec[..])) - } - #[cfg(not(windows))] - pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec) -> InterpResult<'tcx, OsString> { - let s = String::from_utf16(&u16_vec[..]) - .map_err(|_| err_unsup_format!("{:?} is not a valid utf-16 string", u16_vec))?; - Ok(s.into()) - } - - let u16_vec = self.eval_context_ref().memory.read_wide_str(scalar)?; - u16vec_to_osstring(u16_vec) - } - - /// 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, 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( + fn write_scalar_at_offset( &mut self, - os_str: &OsStr, - scalar: Scalar, - size: u64, - ) -> InterpResult<'tcx, (bool, u64)> { - #[cfg(unix)] - fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> { - Ok(std::os::unix::ffi::OsStrExt::as_bytes(os_str)) - } - #[cfg(not(unix))] - fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> { - // On non-unix platforms the best we can do to transform bytes from/to OS strings is to do the - // intermediate transformation into strings. Which invalidates non-utf8 paths that are actually - // valid. - os_str - .to_str() - .map(|s| s.as_bytes()) - .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into()) - } - - 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. - 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, string_length)) - } - - /// Write a Path to the machine memory, adjusting path separators if needed. - fn write_path_to_c_str( - &mut self, - path: &Path, - scalar: Scalar, - size: u64, - ) -> InterpResult<'tcx, (bool, u64)> { - let os_str = path.as_os_str(); - self.write_os_str_to_c_str(os_str, scalar, size) - } - - /// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what - /// the Windows 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_wide_str( - &mut self, - os_str: &OsStr, - mplace: MPlaceTy<'tcx, Tag>, - size: u64, - ) -> InterpResult<'tcx, (bool, u64)> { - #[cfg(windows)] - fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec> { - Ok(std::os::windows::ffi::OsStrExt::encode_wide(os_str).collect()) - } - #[cfg(not(windows))] - fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec> { - // On non-Windows platforms the best we can do to transform Vec from/to OS strings is to do the - // intermediate transformation into strings. Which invalidates non-utf8 paths that are actually - // valid. - os_str - .to_str() - .map(|s| s.encode_utf16().collect()) - .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into()) - } - - let u16_vec = os_str_to_u16vec(os_str)?; - // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required - // 0x0000 terminator to memory would cause an out-of-bounds access. - let string_length = u64::try_from(u16_vec.len()).unwrap(); - if size <= string_length { - return Ok((false, string_length)); - } - + op: OpTy<'tcx, Tag>, + offset: u64, + value: impl Into>, + layout: TyAndLayout<'tcx>, + ) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); - - // Store the UTF-16 string. - let char_size = Size::from_bytes(2); - for (idx, c) in u16_vec.into_iter().chain(iter::once(0x0000)).enumerate() { - let place = this.mplace_field(mplace, idx as u64)?; - this.write_scalar(Scalar::from_uint(c, char_size), place.into())?; - } - Ok((true, string_length)) - } - - /// Dispatches to appropriate implementations for allocating & writing OsString in Memory, - /// depending on the interpretation target. - fn alloc_os_str_as_target_str( - &mut self, - os_str: &OsStr, - memkind: MemoryKind, - ) -> InterpResult<'tcx, Pointer> { - let target_os = self.eval_context_ref().tcx.sess.target.target.target_os.as_str(); - match target_os { - "linux" | "macos" => Ok(self.alloc_os_str_as_c_str(os_str, memkind)), - "windows" => Ok(self.alloc_os_str_as_wide_str(os_str, memkind)), - unsupported => throw_unsup_format!("OsString support for target OS `{}` not yet available", unsupported), - } + let op_place = this.deref_operand(op)?; + let offset = Size::from_bytes(offset); + // Ensure that the following read at an offset is within bounds + assert!(op_place.layout.size >= offset + layout.size); + let value_place = op_place.offset(offset, MemPlaceMeta::None, layout, this)?; + this.write_scalar(value, value_place.into()) } - fn alloc_os_str_as_c_str( + /// Parse a `timespec` struct and return it as a `std::time::Duration`. It returns `None` + /// if the value in the `timespec` struct is invalid. Some libc functions will return + /// `EINVAL` in this case. + fn read_timespec( &mut self, - os_str: &OsStr, - memkind: MemoryKind, - ) -> Pointer { - let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator. + timespec_ptr_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); - - let arg_type = this.tcx.mk_array(this.tcx.types.u8, size); - let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind); - assert!(self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap().0); - arg_place.ptr.assert_ptr() + let tp = this.deref_operand(timespec_ptr_op)?; + let seconds_place = this.mplace_field(tp, 0)?; + let seconds_scalar = this.read_scalar(seconds_place.into())?; + let seconds = seconds_scalar.to_machine_isize(this)?; + let nanoseconds_place = this.mplace_field(tp, 1)?; + let nanoseconds_scalar = this.read_scalar(nanoseconds_place.into())?; + let nanoseconds = nanoseconds_scalar.to_machine_isize(this)?; + + Ok(try { + // tv_sec must be non-negative. + let seconds: u64 = seconds.try_into().ok()?; + // tv_nsec must be non-negative. + let nanoseconds: u32 = nanoseconds.try_into().ok()?; + if nanoseconds >= 1_000_000_000 { + // tv_nsec must not be greater than 999,999,999. + None? + } + Duration::new(seconds, nanoseconds) + }) } +} - fn alloc_os_str_as_wide_str( - &mut self, - os_str: &OsStr, - memkind: MemoryKind, - ) -> Pointer { - let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator. - let this = self.eval_context_mut(); - - let arg_type = this.tcx.mk_array(this.tcx.types.u16, size); - let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind); - assert!(self.write_os_str_to_wide_str(os_str, arg_place, size).unwrap().0); - arg_place.ptr.assert_ptr() +/// Check that the number of args is what we expect. +pub fn check_arg_count<'a, 'tcx, const N: usize>(args: &'a [OpTy<'tcx, Tag>]) -> InterpResult<'tcx, &'a [OpTy<'tcx, Tag>; N]> + where &'a [OpTy<'tcx, Tag>; N]: TryFrom<&'a [OpTy<'tcx, Tag>]> { + if let Ok(ops) = args.try_into() { + return Ok(ops); } + throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N) +} + +pub fn isolation_error(name: &str) -> InterpResult<'static> { + throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!( + "{} not available when isolation is enabled", + name, + ))) } pub fn immty_from_int_checked<'tcx>( int: impl Into, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> { let int = int.into(); Ok(ImmTy::try_from_int(int, layout).ok_or_else(|| { @@ -676,7 +572,7 @@ pub fn immty_from_int_checked<'tcx>( pub fn immty_from_uint_checked<'tcx>( int: impl Into, - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> { let int = int.into(); Ok(ImmTy::try_from_uint(int, layout).ok_or_else(|| {