-use std::ffi::{OsStr, OsString};
-use std::path::{Path, PathBuf};
-use std::{iter, mem};
-use std::convert::TryFrom;
-use std::borrow::Cow;
-
-#[cfg(unix)]
-use std::os::unix::ffi::{OsStrExt, OsStringExt};
-#[cfg(windows)]
-use std::os::windows::ffi::{OsStrExt, OsStringExt};
-
-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<DefId> {
fn eval_path_scalar(
&mut self,
path: &[&str],
- ) -> InterpResult<'tcx, ScalarMaybeUndef<Tag>> {
+ ) -> InterpResult<'tcx, ScalarMaybeUninit<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.eval_to_allocation(cid)?;
let const_val = this.read_scalar(const_val.into())?;
return Ok(const_val);
}
fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar<Tag>> {
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<Tag>> {
+ 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)
}
/// Test if this immediate equals 0.
fn is_null(&self, val: Scalar<Tag>) -> 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)
}
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`.
// 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();
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>,
// 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,
} 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
// `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)
fields: impl Iterator<Item = InterpResult<'tcx, MPlaceTy<'tcx, Tag>>>,
) -> 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::<InterpResult<'tcx, Vec<MPlaceTy<'tcx, Tag>>>>()?;
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,
/// 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,
)
}
+ /// 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<Tag>) -> 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<Tag>> {
- 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<Tag>> {
+ 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
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.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",
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)
}
}
}
- /// 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<Tag>) -> 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(OsStr::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<Tag>> {
let this = self.eval_context_ref();
- let bytes = this.memory.read_c_str(scalar)?;
- bytes_to_os_str(bytes)
+ 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<Tag>) -> InterpResult<'tcx, OsString>
- where
- 'tcx: 'a,
- 'mir: 'a,
- {
- #[cfg(windows)]
- pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsString> {
- Ok(OsString::from_wide(&u16_vec[..]))
- }
- #[cfg(not(windows))]
- pub fn u16vec_to_osstring<'tcx, 'a>(u16_vec: Vec<u16>) -> 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<Tag>,
- size: u64,
- ) -> InterpResult<'tcx, (bool, u64)> {
- #[cfg(unix)]
- fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
- Ok(os_str.as_bytes())
- }
- #[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))
- }
-
- /// 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,
- scalar: Scalar<Tag>,
- size: u64,
- ) -> InterpResult<'tcx, (bool, u64)> {
- #[cfg(windows)]
- fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
- Ok(os_str.encode_wide().collect())
- }
- #[cfg(not(windows))]
- fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
- // On non-Windows platforms the best we can do to transform Vec<u16> 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));
- }
-
- // Store the UTF-16 string.
- self.eval_context_mut()
- .memory
- .write_u16s(scalar, u16_vec.into_iter().chain(iter::once(0x0000)))?;
- Ok((true, string_length))
- }
-
- /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
- fn alloc_os_str_as_c_str(
- &mut self,
- os_str: &OsStr,
- memkind: MemoryKind<MiriMemoryKind>,
- ) -> Pointer<Tag> {
- let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator.
+ op: OpTy<'tcx, Tag>,
+ offset: u64,
+ value: impl Into<ScalarMaybeUninit<Tag>>,
+ layout: TyAndLayout<'tcx>,
+ ) -> InterpResult<'tcx, ()> {
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 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())
}
- /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of `u16`.
- fn alloc_os_str_as_wide_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<MiriMemoryKind>,
- ) -> Pointer<Tag> {
- let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator.
+ timespec_ptr_op: OpTy<'tcx, Tag>,
+ ) -> InterpResult<'tcx, Option<Duration>> {
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.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)
+ })
}
+}
- /// 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<Tag>) -> InterpResult<'tcx, Cow<'a, Path>>
- where
- 'tcx: 'a,
- 'mir: 'a,
- {
- let this = self.eval_context_ref();
- let os_str = this.read_os_str_from_c_str(scalar)?;
-
- #[cfg(windows)]
- return Ok(if this.tcx.sess.target.target.target_os == "windows" {
- // Windows-on-Windows, all fine.
- Cow::Borrowed(Path::new(os_str))
- } else {
- // Unix target, Windows host. Need to convert target '/' to host '\'.
- let converted = os_str
- .encode_wide()
- .map(|wchar| if wchar == '/' as u16 { '\\' as u16 } else { wchar })
- .collect::<Vec<_>>();
- Cow::Owned(PathBuf::from(OsString::from_wide(&converted)))
- });
- #[cfg(unix)]
- return Ok(if this.tcx.sess.target.target.target_os == "windows" {
- // Windows target, Unix host. Need to convert target '\' to host '/'.
- let converted = os_str
- .as_bytes()
- .iter()
- .map(|&wchar| if wchar == '/' as u8 { '\\' as u8 } else { wchar })
- .collect::<Vec<_>>();
- Cow::Owned(PathBuf::from(OsString::from_vec(converted)))
- } else {
- // Unix-on-Unix, all is fine.
- Cow::Borrowed(Path::new(os_str))
- });
+/// 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)
+}
- /// Write a Path to the machine memory, adjusting path separators if needed.
- fn write_path_to_c_str(
- &mut self,
- path: &Path,
- scalar: Scalar<Tag>,
- size: u64,
- ) -> InterpResult<'tcx, (bool, u64)> {
- let this = self.eval_context_mut();
-
- #[cfg(windows)]
- let os_str = if this.tcx.sess.target.target.target_os == "windows" {
- // Windows-on-Windows, all fine.
- Cow::Borrowed(path.as_os_str())
- } else {
- // Unix target, Windows host. Need to convert host '\\' to target '/'.
- let converted = path
- .as_os_str()
- .encode_wide()
- .map(|wchar| if wchar == '\\' as u16 { '/' as u16 } else { wchar })
- .collect::<Vec<_>>();
- Cow::Owned(OsString::from_wide(&converted))
- };
- #[cfg(unix)]
- let os_str = if this.tcx.sess.target.target.target_os == "windows" {
- // Windows target, Unix host. Need to convert host '/' to target '\'.
- let converted = path
- .as_os_str()
- .as_bytes()
- .iter()
- .map(|&wchar| if wchar == '/' as u8 { '\\' as u8 } else { wchar })
- .collect::<Vec<_>>();
- Cow::Owned(OsString::from_vec(converted))
- } else {
- // Unix-on-Unix, all is fine.
- Cow::Borrowed(path.as_os_str())
- };
-
- this.write_os_str_to_c_str(&os_str, scalar, size)
- }
+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<i128>,
- 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(|| {
pub fn immty_from_uint_checked<'tcx>(
int: impl Into<u128>,
- 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(|| {