X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fhelpers.rs;h=36d3181ce4e4b066e9a00bec825fd56b3d684391;hb=d85f09c4e4bdee1d9698dce9a2163f46f1e1697d;hp=c82971810c0212cb1f51a9fe5b4b5998903d2945;hpb=72bd25de83bc1139829656a1b84a4fc04faa0554;p=rust.git diff --git a/src/helpers.rs b/src/helpers.rs index c82971810c0..36d3181ce4e 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,12 +1,15 @@ -use std::mem; -use std::ffi::{OsStr, OsString}; +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, Align, LayoutOf, Size, TyLayout}, + 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; @@ -14,40 +17,71 @@ impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} -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>> { - let this = self.eval_context_ref(); - this.tcx - .crates() - .iter() - .find(|&&krate| this.tcx.original_crate_name(krate).as_str() == path[0]) - .and_then(|krate| { - let krate = DefId { - krate: *krate, - index: CRATE_DEF_INDEX, - }; - let mut items = this.tcx.item_children(krate); - let mut path_it = path.iter().skip(1).peekable(); - - while let Some(segment) = path_it.next() { - for item in mem::replace(&mut items, Default::default()).iter() { - if item.ident.name.as_str() == *segment { - if path_it.peek().is_none() { - return Some(ty::Instance::mono(this.tcx.tcx, item.res.def_id())); - } - - items = this.tcx.item_children(item.res.def_id()); - break; +/// Gets an instance for a path. +fn try_resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option { + tcx.crates() + .iter() + .find(|&&krate| tcx.original_crate_name(krate).as_str() == path[0]) + .and_then(|krate| { + let krate = DefId { krate: *krate, index: CRATE_DEF_INDEX }; + let mut items = tcx.item_children(krate); + let mut path_it = path.iter().skip(1).peekable(); + + while let Some(segment) = path_it.next() { + for item in mem::replace(&mut items, Default::default()).iter() { + if item.ident.name.as_str() == *segment { + if path_it.peek().is_none() { + return Some(item.res.def_id()); } + + items = tcx.item_children(item.res.def_id()); + break; } } - None - }) - .ok_or_else(|| { - let path = path.iter().map(|&s| s.to_owned()).collect(); - err_unsup!(PathNotFound(path)).into() - }) + } + None + }) +} + +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + /// Gets an instance for a 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> { + 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> { + 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`. @@ -65,26 +99,18 @@ fn is_null(&self, val: Scalar) -> InterpResult<'tcx, bool> { /// Turn a Scalar into an Option fn test_null(&self, val: Scalar) -> InterpResult<'tcx, Option>> { let this = self.eval_context_ref(); - Ok(if this.is_null(val)? { - None - } else { - Some(val) - }) + Ok(if this.is_null(val)? { None } else { Some(val) }) } /// Get the `Place` for a local fn local_place(&mut self, local: mir::Local) -> InterpResult<'tcx, PlaceTy<'tcx, Tag>> { let this = self.eval_context_mut(); - let place = mir::Place { base: mir::PlaceBase::Local(local), projection: Box::new([]) }; + let place = mir::Place { local: local, projection: List::empty() }; this.eval_place(&place) } /// Generate some random bytes, and write them to `dest`. - fn gen_random( - &mut self, - ptr: Scalar, - len: usize, - ) -> InterpResult<'tcx> { + fn gen_random(&mut self, ptr: Scalar, 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 @@ -95,26 +121,52 @@ fn gen_random( } let this = self.eval_context_mut(); - // Don't forget the bounds check. - let ptr = this.memory.check_ptr_access( - ptr, - Size::from_bytes(len as u64), - Align::from_bytes(1).unwrap() - )?.expect("we already checked for size 0"); - - 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))?; - } - else { + .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?; + } else { let rng = this.memory.extra.rng.get_mut(); rng.fill_bytes(&mut data); } - this.memory.get_mut(ptr.alloc_id)?.write_bytes(&*this.tcx, ptr, &data) + this.memory.write_bytes(ptr, data.iter().copied()) + } + + /// Call a function: Push the stack frame and pass the arguments. + /// For now, arguments must be scalars (so that the caller does not have to know the layout). + fn call_function( + &mut self, + f: ty::Instance<'tcx>, + args: &[Immediate], + dest: Option>, + stack_pop: StackPopCleanup, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + // 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)?; + + // Initialize arguments. + let mut callee_args = this.frame().body.args_iter(); + for arg in args { + let callee_arg = this.local_place( + callee_args.next().expect("callee has fewer arguments than expected"), + )?; + this.write_immediate(*arg, callee_arg)?; + } + callee_args.next().expect_none("callee has more arguments than expected"); + + Ok(()) } /// Visits the memory covered by `place`, sensitive to freezing: the 3rd parameter @@ -127,10 +179,11 @@ fn visit_freeze_sensitive( ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); trace!("visit_frozen(place={:?}, size={:?})", *place, size); - debug_assert_eq!(size, + debug_assert_eq!( + size, this.size_and_align_of_mplace(place)? - .map(|(size, _)| size) - .unwrap_or_else(|| place.layout.size) + .map(|(size, _)| size) + .unwrap_or_else(|| place.layout.size) ); // Store how far we proceeded into the place so far. Everything to the left of // this offset has already been handled, in the sense that the frozen parts @@ -150,11 +203,11 @@ fn visit_freeze_sensitive( let frozen_size = unsafe_cell_offset - end_offset; // Everything between the end_ptr and this `UnsafeCell` is frozen. if frozen_size != Size::ZERO { - action(end_ptr, frozen_size, /*frozen*/true)?; + action(end_ptr, frozen_size, /*frozen*/ true)?; } // This `UnsafeCell` is NOT frozen. if unsafe_cell_size != Size::ZERO { - action(unsafe_cell_ptr, unsafe_cell_size, /*frozen*/false)?; + action(unsafe_cell_ptr, unsafe_cell_size, /*frozen*/ false)?; } // Update end end_ptr. end_ptr = unsafe_cell_ptr.wrapping_offset(unsafe_cell_size, this); @@ -168,7 +221,8 @@ fn visit_freeze_sensitive( unsafe_cell_action: |place| { trace!("unsafe_cell_action on {:?}", place.ptr); // We need a size to go on. - let unsafe_cell_size = this.size_and_align_of_mplace(place)? + let unsafe_cell_size = this + .size_and_align_of_mplace(place)? .map(|(size, _)| size) // for extern types, just cover what we can .unwrap_or_else(|| place.layout.size); @@ -191,18 +245,17 @@ fn visit_freeze_sensitive( /// Visiting the memory covered by a `MemPlace`, being aware of /// whether we are inside an `UnsafeCell` or not. struct UnsafeCellVisitor<'ecx, 'mir, 'tcx, F> - where F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> + where + F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>, { ecx: &'ecx MiriEvalContext<'mir, 'tcx>, unsafe_cell_action: F, } - impl<'ecx, 'mir, 'tcx, F> - ValueVisitor<'mir, 'tcx, Evaluator<'tcx>> - for - UnsafeCellVisitor<'ecx, 'mir, 'tcx, F> + impl<'ecx, 'mir, 'tcx, F> ValueVisitor<'mir, 'tcx, Evaluator<'tcx>> + for UnsafeCellVisitor<'ecx, 'mir, 'tcx, F> where - F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> + F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>, { type V = MPlaceTy<'tcx, Tag>; @@ -212,11 +265,11 @@ fn ecx(&self) -> &MiriEvalContext<'mir, 'tcx> { } // Hook to detect `UnsafeCell`. - fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> - { + 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 { - ty::Adt(adt, _) => Some(adt.did) == self.ecx.tcx.lang_items().unsafe_cell_type(), + ty::Adt(adt, _) => + Some(adt.did) == self.ecx.tcx.lang_items().unsafe_cell_type(), _ => false, }; if is_unsafe_cell { @@ -253,7 +306,7 @@ fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> fn visit_aggregate( &mut self, place: MPlaceTy<'tcx, Tag>, - fields: impl Iterator>>, + fields: impl Iterator>>, ) -> InterpResult<'tcx> { match place.layout.fields { layout::FieldPlacement::Array { .. } => { @@ -263,7 +316,8 @@ fn visit_aggregate( } layout::FieldPlacement::Arbitrary { .. } => { // Gather the subplaces and sort them before visiting. - let mut places = fields.collect::>>>()?; + let mut places = + fields.collect::>>>()?; places.sort_by_key(|place| place.ptr.assert_ptr().offset); self.walk_aggregate(place, places.into_iter().map(Ok)) } @@ -275,52 +329,23 @@ 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") + if frozen { Ok(()) } else { (self.unsafe_cell_action)(v) } } } } - /// Helper function to get a `libc` constant as a `Scalar`. - fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar> { - 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])?.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( &mut self, - place: &MPlaceTy<'tcx, Tag>, + place: MPlaceTy<'tcx, Tag>, imms: &[ImmTy<'tcx, Tag>], ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -330,7 +355,7 @@ fn write_packed_immediates( for &imm in imms { this.write_immediate_to_mplace( *imm, - place.offset(offset, None, imm.layout, &*this.tcx)?, + place.offset(offset, MemPlaceMeta::None, imm.layout, &*this.tcx)?, )?; offset += imm.layout.size; } @@ -340,12 +365,27 @@ fn write_packed_immediates( /// Helper function used inside the shims of foreign functions to check that isolation is /// disabled. It returns an error using the `name` of the foreign function if this is not the /// case. - fn check_no_isolation(&mut self, name: &str) -> InterpResult<'tcx> { - if !self.eval_context_mut().machine.communicate { - throw_unsup_format!("`{}` not available when isolation is enabled. Pass the flag `-Zmiri-disable-isolation` to disable it.", name) + 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 isolation)", + 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, + target_os, + "`{}` is only available on the `{}` target OS", + name, + target_os, + ) + } /// Sets the last error variable. fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { @@ -355,8 +395,8 @@ fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { } /// Gets the last error variable. - fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); + 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() } @@ -383,11 +423,16 @@ fn set_last_error_from_io_error(&mut self, e: std::io::Error) -> InterpResult<'t TimedOut => "ETIMEDOUT", 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 {}.", target.target_os) + // 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 + ) }; this.set_last_error(last_error) } @@ -397,7 +442,7 @@ fn set_last_error_from_io_error(&mut self, e: std::io::Error) -> InterpResult<'t /// `Ok(-1)` and sets the last OS error accordingly. /// /// This function uses `T: From` instead of `i32` directly because some IO related - /// functions return different integer types (like `read`, that returns an `i64`) + /// functions return different integer types (like `read`, that returns an `i64`). fn try_unwrap_io_result>( &mut self, result: std::io::Result, @@ -413,61 +458,97 @@ fn try_unwrap_io_result>( /// Helper function to read an OsString from a null-terminated sequence of bytes, which is what /// the Unix APIs usually handle. - fn read_os_string_from_c_string(&mut self, scalar: Scalar) -> InterpResult<'tcx, OsString> { - let bytes = self.eval_context_mut().memory.read_c_str(scalar)?; - Ok(bytes_to_os_str(bytes)?.into()) + fn read_os_str_from_c_str<'a>(&'a self, scalar: Scalar) -> InterpResult<'tcx, &'a OsStr> + where + 'tcx: 'a, + 'mir: 'a, + { + #[cfg(target_os = "unix")] + fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> { + Ok(std::os::unix::ffi::OsStringExt::from_bytes(bytes)) + } + #[cfg(not(target_os = "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)) + } + + let this = self.eval_context_ref(); + let bytes = this.memory.read_c_str(scalar)?; + bytes_to_os_str(bytes) } /// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what - /// the Unix APIs usually handle. - fn write_os_str_to_c_string(&mut self, os_str: &OsStr, ptr: Pointer, size: u64) -> InterpResult<'tcx> { + /// 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, + size: u64, + ) -> 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) + } + #[cfg(not(target_os = "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)?; - let len = bytes.len(); // 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 overflow. - if size <= len as u64 { - throw_unsup_format!("OsString of length {} is too large for destination buffer of size {}", len, size) + // 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)); } - let actual_len = (len as u64) - .checked_add(1) - .map(Size::from_bytes) - .ok_or_else(|| err_unsup_format!("OsString of length {} is too large", len))?; - let this = self.eval_context_mut(); - this.memory.check_ptr_access(ptr.into(), actual_len, Align::from_bytes(1).unwrap())?; - let buffer = this.memory.get_mut(ptr.alloc_id)?.get_bytes_mut(&*this.tcx, ptr, actual_len)?; - buffer[..len].copy_from_slice(bytes); - // This is ok because the buffer was strictly larger than `bytes`, so after adding the - // null terminator, the buffer size is larger or equal to `bytes.len()`, meaning that - // `bytes` actually fit inside tbe buffer. - buffer[len] = 0; - Ok(()) + self.eval_context_mut() + .memory + .write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?; + Ok((true, string_length)) } -} -#[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) -} + fn alloc_os_str_as_c_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 `0` terminator. + let this = self.eval_context_mut(); -#[cfg(target_os = "unix")] -fn bytes_to_os_str<'tcx, 'a>(bytes: &'a[u8]) -> InterpResult<'tcx, &'a OsStr> { - Ok(std::os::unix::ffi::OsStringExt::from_bytes(bytes)) + let arg_type = this.tcx.mk_array(this.tcx.types.u8, size); + let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind); + self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap(); + arg_place.ptr.assert_ptr() + } } -// 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. -#[cfg(not(target_os = "unix"))] -fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> { - 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()) +pub fn immty_from_int_checked<'tcx>( + int: impl Into, + 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()) + })?) } -#[cfg(not(target_os = "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)) +pub fn immty_from_uint_checked<'tcx>( + int: impl Into, + 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!("unsigned value {:#x} does not fit in {} bits", int, layout.size.bits()) + })?) }