-use std::convert::TryFrom;
+use std::convert::{TryFrom, TryInto};
use std::mem;
+use std::num::NonZeroUsize;
use log::trace;
use rustc_middle::mir;
-use rustc_middle::ty::{
- self,
- layout::{self, LayoutOf, Size, TyAndLayout},
- List, TyCtxt,
-};
+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 };
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_windows(&mut self, module: &str, name: &str) -> InterpResult<'tcx, Scalar<Tag>> {
self.eval_context_mut()
.eval_path_scalar(&["std", "sys", "windows", module, name])?
- .not_undef()
+ .check_init()
}
/// Helper function to get a `windows` constant as an `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]).monomorphic_ty(*this.tcx);
+ let ty = this.resolve_path(&["std", "sys", "windows", "c", name]).ty(*this.tcx, ty::ParamEnv::reveal_all());
this.layout_of(ty)
}
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>,
} 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`")
}
}
}
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()
+ 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 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 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())
+ }
+
+ fn write_scalar_at_offset(
+ &mut self,
+ op: OpTy<'tcx, Tag>,
+ offset: u64,
+ value: impl Into<ScalarMaybeUninit<Tag>>,
+ layout: TyAndLayout<'tcx>,
+ ) -> InterpResult<'tcx, ()> {
+ let this = self.eval_context_mut();
+ 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())
+ }
+}
+
+/// 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 immty_from_int_checked<'tcx>(