<commit_before>use std::collections::{HashMap, HashSet};
use std::fmt::Write;

use rustc::hir::def_id::DefId;
use rustc::hir::map::definitions::DefPathData;
use rustc::middle::const_val::ConstVal;
use rustc::mir;
use rustc::traits::Reveal;
use rustc::ty::layout::{self, Layout, Size};
use rustc::ty::subst::{Subst, Substs, Kind};
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder};
use rustc::traits;
use rustc_data_structures::indexed_vec::Idx;
use syntax::codemap::{self, DUMMY_SP, Span};
use syntax::ast;
use syntax::abi::Abi;

use error::{EvalError, EvalResult};
use lvalue::{Global, GlobalId, Lvalue, LvalueExtra};
use memory::{Memory, Pointer, TlsKey};
use operator;
use value::{PrimVal, PrimValKind, Value};

pub struct EvalContext<'a, 'tcx: 'a> {
    /// The results of the type checker, from rustc.
    pub(crate) tcx: TyCtxt<'a, 'tcx, 'tcx>,

    /// The virtual memory system.
    pub(crate) memory: Memory<'a, 'tcx>,

    /// Precomputed statics, constants and promoteds.
    pub(crate) globals: HashMap<GlobalId<'tcx>, Global<'tcx>>,

    /// The virtual call stack.
    pub(crate) stack: Vec<Frame<'tcx>>,

    /// The maximum number of stack frames allowed
    pub(crate) stack_limit: usize,

    /// The maximum number of operations that may be executed.
    /// This prevents infinite loops and huge computations from freezing up const eval.
    /// Remove once halting problem is solved.
    pub(crate) steps_remaining: u64,

    /// Environment variables set by `setenv`
    /// Miri does not expose env vars from the host to the emulated program
    pub(crate) env_vars: HashMap<Vec<u8>, Pointer>,
}

/// A stack frame.
pub struct Frame<'tcx> {
    ////////////////////////////////////////////////////////////////////////////////
    // Function and callsite information
    ////////////////////////////////////////////////////////////////////////////////

    /// The MIR for the function called on this frame.
    pub mir: &'tcx mir::Mir<'tcx>,

    /// The def_id and substs of the current function
    pub instance: ty::Instance<'tcx>,

    /// The span of the call site.
    pub span: codemap::Span,

    ////////////////////////////////////////////////////////////////////////////////
    // Return lvalue and locals
    ////////////////////////////////////////////////////////////////////////////////

    /// The block to return to when returning from the current stack frame
    pub return_to_block: StackPopCleanup,

    /// The location where the result of the current stack frame should be written to.
    pub return_lvalue: Lvalue<'tcx>,

    /// The list of locals for this stack frame, stored in order as
    /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Option<Value>`s.
    /// `None` represents a local that is currently dead, while a live local
    /// can either directly contain `PrimVal` or refer to some part of an `Allocation`.
    ///
    /// Before being initialized, arguments are `Value::ByVal(PrimVal::Undef)` and other locals are `None`.
    pub locals: Vec<Option<Value>>,

    ////////////////////////////////////////////////////////////////////////////////
    // Current position within the function
    ////////////////////////////////////////////////////////////////////////////////

    /// The block that is currently executed (or will be executed after the above call stacks
    /// return).
    pub block: mir::BasicBlock,

    /// The index of the currently evaluated statment.
    pub stmt: usize,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum StackPopCleanup {
    /// The stackframe existed to compute the initial value of a static/constant, make sure it
    /// isn't modifyable afterwards in case of constants.
    /// In case of `static mut`, mark the memory to ensure it's never marked as immutable through
    /// references or deallocated
    /// The bool decides whether the value is mutable (true) or not (false)
    MarkStatic(bool),
    /// A regular stackframe added due to a function call will need to get forwarded to the next
    /// block
    Goto(mir::BasicBlock),
    /// After finishing a tls destructor, find the next one instead of starting from the beginning
    /// and thus just rerunning the first one until its `data` argument is null
    ///
    /// The index is the current tls destructor's index
    Tls(Option<TlsKey>),
    /// The main function and diverging functions have nowhere to return to
    None,
}

#[derive(Copy, Clone, Debug)]
pub struct ResourceLimits {
    pub memory_size: u64,
    pub step_limit: u64,
    pub stack_limit: usize,
}

impl Default for ResourceLimits {
    fn default() -> Self {
        ResourceLimits {
            memory_size: 100 * 1024 * 1024, // 100 MB
            step_limit: 1_000_000,
            stack_limit: 100,
        }
    }
}

impl<'a, 'tcx> EvalContext<'a, 'tcx> {
    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self {
        EvalContext {
            tcx,
            memory: Memory::new(&tcx.data_layout, limits.memory_size),
            globals: HashMap::new(),
            stack: Vec::new(),
            stack_limit: limits.stack_limit,
            steps_remaining: limits.step_limit,
            env_vars: HashMap::new(),
        }
    }

    pub fn alloc_ptr(&mut self, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> {
        let substs = self.substs();
        self.alloc_ptr_with_substs(ty, substs)
    }

    pub fn alloc_ptr_with_substs(
        &mut self,
        ty: Ty<'tcx>,
        substs: &'tcx Substs<'tcx>
    ) -> EvalResult<'tcx, Pointer> {
        let size = self.type_size_with_substs(ty, substs)?.expect("cannot alloc memory for unsized type");
        let align = self.type_align_with_substs(ty, substs)?;
        self.memory.allocate(size, align)
    }

    pub fn memory(&self) -> &Memory<'a, 'tcx> {
        &self.memory
    }

    pub fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx> {
        &mut self.memory
    }

    pub fn stack(&self) -> &[Frame<'tcx>] {
        &self.stack
    }

    pub(crate) fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> {
        let ptr = self.memory.allocate_cached(s.as_bytes())?;
        Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128)))
    }

    pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>) -> EvalResult<'tcx, Value> {
        use rustc::middle::const_val::ConstVal::*;
        use rustc_const_math::ConstFloat;

        let primval = match *const_val {
            Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()),

            Float(ConstFloat::F32(f)) => PrimVal::from_f32(f),
            Float(ConstFloat::F64(f)) => PrimVal::from_f64(f),

            Bool(b) => PrimVal::from_bool(b),
            Char(c) => PrimVal::from_char(c),

            Str(ref s) => return self.str_to_value(s),

            ByteStr(ref bs) => {
                let ptr = self.memory.allocate_cached(bs)?;
                PrimVal::Ptr(ptr)
            }

            Variant(_)   => unimplemented!(),
            Struct(_)    => unimplemented!(),
            Tuple(_)     => unimplemented!(),
            // function items are zero sized and thus have no readable value
            Function(..)  => PrimVal::Undef,
            Array(_)     => unimplemented!(),
            Repeat(_, _) => unimplemented!(),
        };

        Ok(Value::ByVal(primval))
    }

    pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
        // generics are weird, don't run this function on a generic
        assert!(!ty.needs_subst());
        ty.is_sized(self.tcx, ty::ParamEnv::empty(Reveal::All), DUMMY_SP)
    }

    pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, &'tcx mir::Mir<'tcx>> {
        trace!("load mir {:?}", instance);
        match instance {
            ty::InstanceDef::Item(def_id) => self.tcx.maybe_optimized_mir(def_id).ok_or_else(|| EvalError::NoMirFor(self.tcx.item_path_str(def_id))),
            _ => Ok(self.tcx.instance_mir(instance)),
        }
    }

    pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
        // miri doesn't care about lifetimes, and will choke on some crazy ones
        // let's simply get rid of them
        let without_lifetimes = self.tcx.erase_regions(&ty);
        let substituted = without_lifetimes.subst(self.tcx, substs);
        self.tcx.normalize_associated_type(&substituted)
    }

    pub fn erase_lifetimes<T>(&self, value: &Binder<T>) -> T
        where T : TypeFoldable<'tcx>
    {
        let value = self.tcx.erase_late_bound_regions(value);
        self.tcx.erase_regions(&value)
    }

    pub(super) fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<u64>> {
        self.type_size_with_substs(ty, self.substs())
    }

    pub(super) fn type_align(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, u64> {
        self.type_align_with_substs(ty, self.substs())
    }

    fn type_size_with_substs(
        &self,
        ty: Ty<'tcx>,
        substs: &'tcx Substs<'tcx>,
    ) -> EvalResult<'tcx, Option<u64>> {
        let layout = self.type_layout_with_substs(ty, substs)?;
        if layout.is_unsized() {
            Ok(None)
        } else {
            Ok(Some(layout.size(&self.tcx.data_layout).bytes()))
        }
    }

    fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, u64> {
        self.type_layout_with_substs(ty, substs).map(|layout| layout.align(&self.tcx.data_layout).abi())
    }

    pub(super) fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, &'tcx Layout> {
        self.type_layout_with_substs(ty, self.substs())
    }

    fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, &'tcx Layout> {
        // TODO(solson): Is this inefficient? Needs investigation.
        let ty = self.monomorphize(ty, substs);

        ty.layout(self.tcx, ty::ParamEnv::empty(Reveal::All)).map_err(EvalError::Layout)
    }

    pub fn push_stack_frame(
        &mut self,
        instance: ty::Instance<'tcx>,
        span: codemap::Span,
        mir: &'tcx mir::Mir<'tcx>,
        return_lvalue: Lvalue<'tcx>,
        return_to_block: StackPopCleanup,
    ) -> EvalResult<'tcx> {
        ::log_settings::settings().indentation += 1;

        /// Return the set of locals that have a storage annotation anywhere
        fn collect_storage_annotations<'tcx>(mir: &'tcx mir::Mir<'tcx>) -> HashSet<mir::Local> {
            use rustc::mir::StatementKind::*;

            let mut set = HashSet::new();
            for block in mir.basic_blocks() {
                for stmt in block.statements.iter() {
                    match stmt.kind {
                        StorageLive(mir::Lvalue::Local(local)) | StorageDead(mir::Lvalue::Local(local)) => {
                            set.insert(local);
                        }
                        _ => {}
                    }
                }
            };
            set
        }

        // Subtract 1 because `local_decls` includes the ReturnPointer, but we don't store a local
        // `Value` for that.
        let annotated_locals = collect_storage_annotations(mir);
        let num_locals = mir.local_decls.len() - 1;
        let mut locals = vec![None; num_locals];
        for i in 0..num_locals {
            let local = mir::Local::new(i+1);
            if !annotated_locals.contains(&local) {
                locals[i] = Some(Value::ByVal(PrimVal::Undef));
            }
        }

        self.stack.push(Frame {
            mir,
            block: mir::START_BLOCK,
            return_to_block,
            return_lvalue,
            locals,
            span,
            instance,
            stmt: 0,
        });

        if self.stack.len() > self.stack_limit {
            Err(EvalError::StackFrameLimitReached)
        } else {
            Ok(())
        }
    }

    pub(super) fn pop_stack_frame(&mut self) -> EvalResult<'tcx> {
        ::log_settings::settings().indentation -= 1;
        let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none");
        match frame.return_to_block {
            StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue {
                let global_value = self.globals.get_mut(&id)
                    .expect("global should have been cached (static)");
                match global_value.value {
                    Value::ByRef(ptr) => self.memory.mark_static_initalized(ptr.alloc_id, mutable)?,
                    Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val {
                        self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?;
                    },
                    Value::ByValPair(val1, val2) => {
                        if let PrimVal::Ptr(ptr) = val1 {
                            self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?;
                        }
                        if let PrimVal::Ptr(ptr) = val2 {
                            self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?;
                        }
                    },
                }
                // see comment on `initialized` field
                assert!(!global_value.initialized);
                global_value.initialized = true;
                assert!(global_value.mutable);
                global_value.mutable = mutable;
            } else {
                bug!("StackPopCleanup::MarkStatic on: {:?}", frame.return_lvalue);
            },
            StackPopCleanup::Goto(target) => self.goto_block(target),
            StackPopCleanup::None => {},
            StackPopCleanup::Tls(key) => {
                // either fetch the next dtor or start new from the beginning, if any are left with a non-null data
                let dtor = match self.memory.fetch_tls_dtor(key)? {
                    dtor @ Some(_) => dtor,
                    None => self.memory.fetch_tls_dtor(None)?,
                };
                if let Some((instance, ptr, key)) = dtor {
                    trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
                    // TODO: Potentially, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs
                    let mir = self.load_mir(instance.def)?;
                    self.push_stack_frame(
                        instance,
                        mir.span,
                        mir,
                        Lvalue::undef(),
                        StackPopCleanup::Tls(Some(key)),
                    )?;
                    let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?;
                    let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
                    let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
                    self.write_primval(dest, ptr, ty)?;
                }
            }
        }
        // deallocate all locals that are backed by an allocation
        for local in frame.locals {
            self.deallocate_local(local)?;
        }

        Ok(())
    }

    pub fn deallocate_local(&mut self, local: Option<Value>) -> EvalResult<'tcx> {
        if let Some(Value::ByRef(ptr)) = local {
            trace!("deallocating local");
            self.memory.dump_alloc(ptr.alloc_id);
            match self.memory.deallocate(ptr) {
                // We could alternatively check whether the alloc_id is static before calling
                // deallocate, but this is much simpler and is probably the rare case.
                Ok(()) | Err(EvalError::DeallocatedStaticMemory) => {},
                other => return other,
            }
        };
        Ok(())
    }

    pub fn assign_discr_and_fields<
        V: IntoValTyPair<'tcx>,
        J: IntoIterator<Item = V>,
    >(
        &mut self,
        dest: Lvalue<'tcx>,
        dest_ty: Ty<'tcx>,
        discr_offset: u64,
        operands: J,
        discr_val: u128,
        variant_idx: usize,
        discr_size: u64,
    ) -> EvalResult<'tcx>
        where J::IntoIter: ExactSizeIterator,
    {
        // FIXME(solson)
        let dest_ptr = self.force_allocation(dest)?.to_ptr()?;

        let discr_dest = dest_ptr.offset(discr_offset, self.memory.layout)?;
        self.memory.write_uint(discr_dest, discr_val, discr_size)?;

        let dest = Lvalue::Ptr {
            ptr: PrimVal::Ptr(dest_ptr),
            extra: LvalueExtra::DowncastVariant(variant_idx),
        };

        self.assign_fields(dest, dest_ty, operands)
    }

    pub fn assign_fields<
        V: IntoValTyPair<'tcx>,
        J: IntoIterator<Item = V>,
    >(
        &mut self,
        dest: Lvalue<'tcx>,
        dest_ty: Ty<'tcx>,
        operands: J,
    ) -> EvalResult<'tcx>
        where J::IntoIter: ExactSizeIterator,
    {
        if self.type_size(dest_ty)? == Some(0) {
            // zst assigning is a nop
            return Ok(());
        }
        if self.ty_to_primval_kind(dest_ty).is_ok() {
            let mut iter = operands.into_iter();
            assert_eq!(iter.len(), 1);
            let (value, value_ty) = iter.next().unwrap().into_val_ty_pair(self)?;
            return self.write_value(value, dest, value_ty);
        }
        for (field_index, operand) in operands.into_iter().enumerate() {
            let (value, value_ty) = operand.into_val_ty_pair(self)?;
            let field_dest = self.lvalue_field(dest, field_index, dest_ty, value_ty)?;
            self.write_value(value, field_dest, value_ty)?;
        }
        Ok(())
    }

    /// Evaluate an assignment statement.
    ///
    /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
    /// type writes its results directly into the memory specified by the lvalue.
    pub(super) fn eval_rvalue_into_lvalue(
        &mut self,
        rvalue: &mir::Rvalue<'tcx>,
        lvalue: &mir::Lvalue<'tcx>,
    ) -> EvalResult<'tcx> {
        let dest = self.eval_lvalue(lvalue)?;
        let dest_ty = self.lvalue_ty(lvalue);
        let dest_layout = self.type_layout(dest_ty)?;

        use rustc::mir::Rvalue::*;
        match *rvalue {
            Use(ref operand) => {
                let value = self.eval_operand(operand)?;
                self.write_value(value, dest, dest_ty)?;
            }

            BinaryOp(bin_op, ref left, ref right) => {
                if self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)? {
                    // There was an overflow in an unchecked binop.  Right now, we consider this an error and bail out.
                    // The rationale is that the reason rustc emits unchecked binops in release mode (vs. the checked binops
                    // it emits in debug mode) is performance, but it doesn't cost us any performance in miri.
                    // If, however, the compiler ever starts transforming unchecked intrinsics into unchecked binops,
                    // we have to go back to just ignoring the overflow here.
                    return Err(EvalError::OverflowingMath);
                }
            }

            CheckedBinaryOp(bin_op, ref left, ref right) => {
                self.intrinsic_with_overflow(bin_op, left, right, dest, dest_ty)?;
            }

            UnaryOp(un_op, ref operand) => {
                let val = self.eval_operand_to_primval(operand)?;
                let kind = self.ty_to_primval_kind(dest_ty)?;
                self.write_primval(dest, operator::unary_op(un_op, val, kind)?, dest_ty)?;
            }

            // Skip everything for zsts
            Aggregate(..) if self.type_size(dest_ty)? == Some(0) => {}

            Aggregate(ref kind, ref operands) => {
                self.inc_step_counter_and_check_limit(operands.len() as u64)?;
                use rustc::ty::layout::Layout::*;
                match *dest_layout {
                    Univariant { ref variant, .. } => {
                        if variant.packed {
                            let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
                            self.memory.mark_packed(ptr, variant.stride().bytes());
                        }
                        self.assign_fields(dest, dest_ty, operands)?;
                    }

                    Array { .. } => {
                        self.assign_fields(dest, dest_ty, operands)?;
                    }

                    General { discr, ref variants, .. } => {
                        if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind {
                            let discr_val = adt_def.discriminants(self.tcx)
                                .nth(variant)
                                .expect("broken mir: Adt variant id invalid")
                                .to_u128_unchecked();
                            let discr_size = discr.size().bytes();
                            if variants[variant].packed {
                                let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
                                self.memory.mark_packed(ptr, variants[variant].stride().bytes());
                            }

                            self.assign_discr_and_fields(
                                dest,
                                dest_ty,
                                variants[variant].offsets[0].bytes(),
                                operands,
                                discr_val,
                                variant,
                                discr_size,
                            )?;
                        } else {
                            bug!("tried to assign {:?} to Layout::General", kind);
                        }
                    }

                    RawNullablePointer { nndiscr, .. } => {
                        if let mir::AggregateKind::Adt(_, variant, _, _) = **kind {
                            if nndiscr == variant as u64 {
                                assert_eq!(operands.len(), 1);
                                let operand = &operands[0];
                                let value = self.eval_operand(operand)?;
                                let value_ty = self.operand_ty(operand);
                                self.write_value(value, dest, value_ty)?;
                            } else {
                                if let Some(operand) = operands.get(0) {
                                    assert_eq!(operands.len(), 1);
                                    let operand_ty = self.operand_ty(operand);
                                    assert_eq!(self.type_size(operand_ty)?, Some(0));
                                }
                                self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
                            }
                        } else {
                            bug!("tried to assign {:?} to Layout::RawNullablePointer", kind);
                        }
                    }

                    StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => {
                        if let mir::AggregateKind::Adt(_, variant, _, _) = **kind {
                            if nonnull.packed {
                                let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
                                self.memory.mark_packed(ptr, nonnull.stride().bytes());
                            }
                            if nndiscr == variant as u64 {
                                self.assign_fields(dest, dest_ty, operands)?;
                            } else {
                                for operand in operands {
                                    let operand_ty = self.operand_ty(operand);
                                    assert_eq!(self.type_size(operand_ty)?, Some(0));
                                }
                                let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;

                                // FIXME(solson)
                                let dest = self.force_allocation(dest)?.to_ptr()?;

                                let dest = dest.offset(offset.bytes(), self.memory.layout)?;
                                let dest_size = self.type_size(ty)?
                                    .expect("bad StructWrappedNullablePointer discrfield");
                                self.memory.write_int(dest, 0, dest_size)?;
                            }
                        } else {
                            bug!("tried to assign {:?} to Layout::RawNullablePointer", kind);
                        }
                    }

                    CEnum { .. } => {
                        assert_eq!(operands.len(), 0);
                        if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind {
                            let n = adt_def.discriminants(self.tcx)
                                .nth(variant)
                                .expect("broken mir: Adt variant index invalid")
                                .to_u128_unchecked();
                            self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?;
                        } else {
                            bug!("tried to assign {:?} to Layout::CEnum", kind);
                        }
                    }

                    Vector { count, .. } => {
                        debug_assert_eq!(count, operands.len() as u64);
                        self.assign_fields(dest, dest_ty, operands)?;
                    }

                    UntaggedUnion { .. } => {
                        assert_eq!(operands.len(), 1);
                        let operand = &operands[0];
                        let value = self.eval_operand(operand)?;
                        let value_ty = self.operand_ty(operand);
                        self.write_value(value, dest, value_ty)?;
                    }

                    _ => {
                        return Err(EvalError::Unimplemented(format!(
                            "can't handle destination layout {:?} when assigning {:?}",
                            dest_layout,
                            kind
                        )));
                    }
                }
            }

            Repeat(ref operand, _) => {
                let (elem_ty, length) = match dest_ty.sty {
                    ty::TyArray(elem_ty, n) => (elem_ty, n as u64),
                    _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty),
                };
                self.inc_step_counter_and_check_limit(length)?;
                let elem_size = self.type_size(elem_ty)?
                    .expect("repeat element type must be sized");
                let value = self.eval_operand(operand)?;

                // FIXME(solson)
                let dest = PrimVal::Ptr(self.force_allocation(dest)?.to_ptr()?);

                for i in 0..length {
                    let elem_dest = dest.offset(i * elem_size, self.memory.layout)?;
                    self.write_value_to_ptr(value, elem_dest, elem_ty)?;
                }
            }

            Len(ref lvalue) => {
                let src = self.eval_lvalue(lvalue)?;
                let ty = self.lvalue_ty(lvalue);
                let (_, len) = src.elem_ty_and_len(ty);
                self.write_primval(dest, PrimVal::from_u128(len as u128), dest_ty)?;
            }

            Ref(_, _, ref lvalue) => {
                let src = self.eval_lvalue(lvalue)?;
                let (ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra();
                let ty = self.lvalue_ty(lvalue);

                let val = match extra {
                    LvalueExtra::None => Value::ByVal(ptr),
                    LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_u128(len as u128)),
                    LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Ptr(vtable)),
                    LvalueExtra::DowncastVariant(..) =>
                        bug!("attempted to take a reference to an enum downcast lvalue"),
                };

                // Check alignment and non-NULLness.
                let (_, align) = self.size_and_align_of_dst(ty, val)?;
                match ptr {
                    PrimVal::Ptr(ptr) => {
                        if !ptr.points_to_zst() { // assume ZST pointer to be always fully alignd (and anyway ZST pointers are going to disappear soon)
                            self.memory.check_align(ptr, align, 0)?;
                        }
                    }
                    PrimVal::Bytes(bytes) => {
                        let v = ((bytes as u128) % (1 << self.memory.pointer_size())) as u64;
                        if v == 0 {
                            return Err(EvalError::InvalidNullPointerUsage);
                        }
                        if v % align != 0 {
                            return Err(EvalError::AlignmentCheckFailed {
                                has: v % align,
                                required: align,
                            });
                        }
                    }
                    PrimVal::Undef => {
                        return Err(EvalError::ReadUndefBytes);
                    }
                }
<commit_msg>fix build after rebase<commit_after>use std::collections::{HashMap, HashSet};
use std::fmt::Write;

use rustc::hir::def_id::DefId;
use rustc::hir::map::definitions::DefPathData;
use rustc::middle::const_val::ConstVal;
use rustc::mir;
use rustc::traits::Reveal;
use rustc::ty::layout::{self, Layout, Size};
use rustc::ty::subst::{Subst, Substs, Kind};
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder};
use rustc::traits;
use rustc_data_structures::indexed_vec::Idx;
use syntax::codemap::{self, DUMMY_SP, Span};
use syntax::ast;
use syntax::abi::Abi;

use error::{EvalError, EvalResult};
use lvalue::{Global, GlobalId, Lvalue, LvalueExtra};
use memory::{Memory, Pointer, TlsKey};
use operator;
use value::{PrimVal, PrimValKind, Value};

pub struct EvalContext<'a, 'tcx: 'a> {
    /// The results of the type checker, from rustc.
    pub(crate) tcx: TyCtxt<'a, 'tcx, 'tcx>,

    /// The virtual memory system.
    pub(crate) memory: Memory<'a, 'tcx>,

    /// Precomputed statics, constants and promoteds.
    pub(crate) globals: HashMap<GlobalId<'tcx>, Global<'tcx>>,

    /// The virtual call stack.
    pub(crate) stack: Vec<Frame<'tcx>>,

    /// The maximum number of stack frames allowed
    pub(crate) stack_limit: usize,

    /// The maximum number of operations that may be executed.
    /// This prevents infinite loops and huge computations from freezing up const eval.
    /// Remove once halting problem is solved.
    pub(crate) steps_remaining: u64,

    /// Environment variables set by `setenv`
    /// Miri does not expose env vars from the host to the emulated program
    pub(crate) env_vars: HashMap<Vec<u8>, Pointer>,
}

/// A stack frame.
pub struct Frame<'tcx> {
    ////////////////////////////////////////////////////////////////////////////////
    // Function and callsite information
    ////////////////////////////////////////////////////////////////////////////////

    /// The MIR for the function called on this frame.
    pub mir: &'tcx mir::Mir<'tcx>,

    /// The def_id and substs of the current function
    pub instance: ty::Instance<'tcx>,

    /// The span of the call site.
    pub span: codemap::Span,

    ////////////////////////////////////////////////////////////////////////////////
    // Return lvalue and locals
    ////////////////////////////////////////////////////////////////////////////////

    /// The block to return to when returning from the current stack frame
    pub return_to_block: StackPopCleanup,

    /// The location where the result of the current stack frame should be written to.
    pub return_lvalue: Lvalue<'tcx>,

    /// The list of locals for this stack frame, stored in order as
    /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Option<Value>`s.
    /// `None` represents a local that is currently dead, while a live local
    /// can either directly contain `PrimVal` or refer to some part of an `Allocation`.
    ///
    /// Before being initialized, arguments are `Value::ByVal(PrimVal::Undef)` and other locals are `None`.
    pub locals: Vec<Option<Value>>,

    ////////////////////////////////////////////////////////////////////////////////
    // Current position within the function
    ////////////////////////////////////////////////////////////////////////////////

    /// The block that is currently executed (or will be executed after the above call stacks
    /// return).
    pub block: mir::BasicBlock,

    /// The index of the currently evaluated statment.
    pub stmt: usize,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum StackPopCleanup {
    /// The stackframe existed to compute the initial value of a static/constant, make sure it
    /// isn't modifyable afterwards in case of constants.
    /// In case of `static mut`, mark the memory to ensure it's never marked as immutable through
    /// references or deallocated
    /// The bool decides whether the value is mutable (true) or not (false)
    MarkStatic(bool),
    /// A regular stackframe added due to a function call will need to get forwarded to the next
    /// block
    Goto(mir::BasicBlock),
    /// After finishing a tls destructor, find the next one instead of starting from the beginning
    /// and thus just rerunning the first one until its `data` argument is null
    ///
    /// The index is the current tls destructor's index
    Tls(Option<TlsKey>),
    /// The main function and diverging functions have nowhere to return to
    None,
}

#[derive(Copy, Clone, Debug)]
pub struct ResourceLimits {
    pub memory_size: u64,
    pub step_limit: u64,
    pub stack_limit: usize,
}

impl Default for ResourceLimits {
    fn default() -> Self {
        ResourceLimits {
            memory_size: 100 * 1024 * 1024, // 100 MB
            step_limit: 1_000_000,
            stack_limit: 100,
        }
    }
}

impl<'a, 'tcx> EvalContext<'a, 'tcx> {
    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self {
        EvalContext {
            tcx,
            memory: Memory::new(&tcx.data_layout, limits.memory_size),
            globals: HashMap::new(),
            stack: Vec::new(),
            stack_limit: limits.stack_limit,
            steps_remaining: limits.step_limit,
            env_vars: HashMap::new(),
        }
    }

    pub fn alloc_ptr(&mut self, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> {
        let substs = self.substs();
        self.alloc_ptr_with_substs(ty, substs)
    }

    pub fn alloc_ptr_with_substs(
        &mut self,
        ty: Ty<'tcx>,
        substs: &'tcx Substs<'tcx>
    ) -> EvalResult<'tcx, Pointer> {
        let size = self.type_size_with_substs(ty, substs)?.expect("cannot alloc memory for unsized type");
        let align = self.type_align_with_substs(ty, substs)?;
        self.memory.allocate(size, align)
    }

    pub fn memory(&self) -> &Memory<'a, 'tcx> {
        &self.memory
    }

    pub fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx> {
        &mut self.memory
    }

    pub fn stack(&self) -> &[Frame<'tcx>] {
        &self.stack
    }

    pub(crate) fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> {
        let ptr = self.memory.allocate_cached(s.as_bytes())?;
        Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128)))
    }

    pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>) -> EvalResult<'tcx, Value> {
        use rustc::middle::const_val::ConstVal::*;
        use rustc_const_math::ConstFloat;

        let primval = match *const_val {
            Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()),

            Float(ConstFloat::F32(f)) => PrimVal::from_f32(f),
            Float(ConstFloat::F64(f)) => PrimVal::from_f64(f),

            Bool(b) => PrimVal::from_bool(b),
            Char(c) => PrimVal::from_char(c),

            Str(ref s) => return self.str_to_value(s),

            ByteStr(ref bs) => {
                let ptr = self.memory.allocate_cached(bs)?;
                PrimVal::Ptr(ptr)
            }

            Variant(_)   => unimplemented!(),
            Struct(_)    => unimplemented!(),
            Tuple(_)     => unimplemented!(),
            // function items are zero sized and thus have no readable value
            Function(..)  => PrimVal::Undef,
            Array(_)     => unimplemented!(),
            Repeat(_, _) => unimplemented!(),
        };

        Ok(Value::ByVal(primval))
    }

    pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
        // generics are weird, don't run this function on a generic
        assert!(!ty.needs_subst());
        ty.is_sized(self.tcx, ty::ParamEnv::empty(Reveal::All), DUMMY_SP)
    }

    pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, &'tcx mir::Mir<'tcx>> {
        trace!("load mir {:?}", instance);
        match instance {
            ty::InstanceDef::Item(def_id) => self.tcx.maybe_optimized_mir(def_id).ok_or_else(|| EvalError::NoMirFor(self.tcx.item_path_str(def_id))),
            _ => Ok(self.tcx.instance_mir(instance)),
        }
    }

    pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
        // miri doesn't care about lifetimes, and will choke on some crazy ones
        // let's simply get rid of them
        let without_lifetimes = self.tcx.erase_regions(&ty);
        let substituted = without_lifetimes.subst(self.tcx, substs);
        self.tcx.normalize_associated_type(&substituted)
    }

    pub fn erase_lifetimes<T>(&self, value: &Binder<T>) -> T
        where T : TypeFoldable<'tcx>
    {
        let value = self.tcx.erase_late_bound_regions(value);
        self.tcx.erase_regions(&value)
    }

    pub(super) fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<u64>> {
        self.type_size_with_substs(ty, self.substs())
    }

    pub(super) fn type_align(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, u64> {
        self.type_align_with_substs(ty, self.substs())
    }

    fn type_size_with_substs(
        &self,
        ty: Ty<'tcx>,
        substs: &'tcx Substs<'tcx>,
    ) -> EvalResult<'tcx, Option<u64>> {
        let layout = self.type_layout_with_substs(ty, substs)?;
        if layout.is_unsized() {
            Ok(None)
        } else {
            Ok(Some(layout.size(&self.tcx.data_layout).bytes()))
        }
    }

    fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, u64> {
        self.type_layout_with_substs(ty, substs).map(|layout| layout.align(&self.tcx.data_layout).abi())
    }

    pub(super) fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, &'tcx Layout> {
        self.type_layout_with_substs(ty, self.substs())
    }

    fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, &'tcx Layout> {
        // TODO(solson): Is this inefficient? Needs investigation.
        let ty = self.monomorphize(ty, substs);

        ty.layout(self.tcx, ty::ParamEnv::empty(Reveal::All)).map_err(EvalError::Layout)
    }

    pub fn push_stack_frame(
        &mut self,
        instance: ty::Instance<'tcx>,
        span: codemap::Span,
        mir: &'tcx mir::Mir<'tcx>,
        return_lvalue: Lvalue<'tcx>,
        return_to_block: StackPopCleanup,
    ) -> EvalResult<'tcx> {
        ::log_settings::settings().indentation += 1;

        /// Return the set of locals that have a storage annotation anywhere
        fn collect_storage_annotations<'tcx>(mir: &'tcx mir::Mir<'tcx>) -> HashSet<mir::Local> {
            use rustc::mir::StatementKind::*;

            let mut set = HashSet::new();
            for block in mir.basic_blocks() {
                for stmt in block.statements.iter() {
                    match stmt.kind {
                        StorageLive(mir::Lvalue::Local(local)) | StorageDead(mir::Lvalue::Local(local)) => {
                            set.insert(local);
                        }
                        _ => {}
                    }
                }
            };
            set
        }

        // Subtract 1 because `local_decls` includes the ReturnPointer, but we don't store a local
        // `Value` for that.
        let annotated_locals = collect_storage_annotations(mir);
        let num_locals = mir.local_decls.len() - 1;
        let mut locals = vec![None; num_locals];
        for i in 0..num_locals {
            let local = mir::Local::new(i+1);
            if !annotated_locals.contains(&local) {
                locals[i] = Some(Value::ByVal(PrimVal::Undef));
            }
        }

        self.stack.push(Frame {
            mir,
            block: mir::START_BLOCK,
            return_to_block,
            return_lvalue,
            locals,
            span,
            instance,
            stmt: 0,
        });

        if self.stack.len() > self.stack_limit {
            Err(EvalError::StackFrameLimitReached)
        } else {
            Ok(())
        }
    }

    pub(super) fn pop_stack_frame(&mut self) -> EvalResult<'tcx> {
        ::log_settings::settings().indentation -= 1;
        let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none");
        match frame.return_to_block {
            StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue {
                let global_value = self.globals.get_mut(&id)
                    .expect("global should have been cached (static)");
                match global_value.value {
                    Value::ByRef(ptr) => self.memory.mark_static_initalized(ptr.alloc_id, mutable)?,
                    Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val {
                        self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?;
                    },
                    Value::ByValPair(val1, val2) => {
                        if let PrimVal::Ptr(ptr) = val1 {
                            self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?;
                        }
                        if let PrimVal::Ptr(ptr) = val2 {
                            self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?;
                        }
                    },
                }
                // see comment on `initialized` field
                assert!(!global_value.initialized);
                global_value.initialized = true;
                assert!(global_value.mutable);
                global_value.mutable = mutable;
            } else {
                bug!("StackPopCleanup::MarkStatic on: {:?}", frame.return_lvalue);
            },
            StackPopCleanup::Goto(target) => self.goto_block(target),
            StackPopCleanup::None => {},
            StackPopCleanup::Tls(key) => {
                // either fetch the next dtor or start new from the beginning, if any are left with a non-null data
                let dtor = match self.memory.fetch_tls_dtor(key)? {
                    dtor @ Some(_) => dtor,
                    None => self.memory.fetch_tls_dtor(None)?,
                };
                if let Some((instance, ptr, key)) = dtor {
                    trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
                    // TODO: Potentially, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs
                    let mir = self.load_mir(instance.def)?;
                    self.push_stack_frame(
                        instance,
                        mir.span,
                        mir,
                        Lvalue::undef(),
                        StackPopCleanup::Tls(Some(key)),
                    )?;
                    let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?;
                    let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
                    let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
                    self.write_primval(dest, ptr, ty)?;
                }
            }
        }
        // deallocate all locals that are backed by an allocation
        for local in frame.locals {
            self.deallocate_local(local)?;
        }

        Ok(())
    }

    pub fn deallocate_local(&mut self, local: Option<Value>) -> EvalResult<'tcx> {
        if let Some(Value::ByRef(ptr)) = local {
            trace!("deallocating local");
            self.memory.dump_alloc(ptr.alloc_id);
            match self.memory.deallocate(ptr) {
                // We could alternatively check whether the alloc_id is static before calling
                // deallocate, but this is much simpler and is probably the rare case.
                Ok(()) | Err(EvalError::DeallocatedStaticMemory) => {},
                other => return other,
            }
        };
        Ok(())
    }

    pub fn assign_discr_and_fields<
        V: IntoValTyPair<'tcx>,
        J: IntoIterator<Item = V>,
    >(
        &mut self,
        dest: Lvalue<'tcx>,
        dest_ty: Ty<'tcx>,
        discr_offset: u64,
        operands: J,
        discr_val: u128,
        variant_idx: usize,
        discr_size: u64,
    ) -> EvalResult<'tcx>
        where J::IntoIter: ExactSizeIterator,
    {
        // FIXME(solson)
        let dest_ptr = self.force_allocation(dest)?.to_ptr()?;

        let discr_dest = dest_ptr.offset(discr_offset, self.memory.layout)?;
        self.memory.write_uint(discr_dest, discr_val, discr_size)?;

        let dest = Lvalue::Ptr {
            ptr: PrimVal::Ptr(dest_ptr),
            extra: LvalueExtra::DowncastVariant(variant_idx),
        };

        self.assign_fields(dest, dest_ty, operands)
    }

    pub fn assign_fields<
        V: IntoValTyPair<'tcx>,
        J: IntoIterator<Item = V>,
    >(
        &mut self,
        dest: Lvalue<'tcx>,
        dest_ty: Ty<'tcx>,
        operands: J,
    ) -> EvalResult<'tcx>
        where J::IntoIter: ExactSizeIterator,
    {
        if self.type_size(dest_ty)? == Some(0) {
            // zst assigning is a nop
            return Ok(());
        }
        if self.ty_to_primval_kind(dest_ty).is_ok() {
            let mut iter = operands.into_iter();
            assert_eq!(iter.len(), 1);
            let (value, value_ty) = iter.next().unwrap().into_val_ty_pair(self)?;
            return self.write_value(value, dest, value_ty);
        }
        for (field_index, operand) in operands.into_iter().enumerate() {
            let (value, value_ty) = operand.into_val_ty_pair(self)?;
            let field_dest = self.lvalue_field(dest, field_index, dest_ty, value_ty)?;
            self.write_value(value, field_dest, value_ty)?;
        }
        Ok(())
    }

    /// Evaluate an assignment statement.
    ///
    /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
    /// type writes its results directly into the memory specified by the lvalue.
    pub(super) fn eval_rvalue_into_lvalue(
        &mut self,
        rvalue: &mir::Rvalue<'tcx>,
        lvalue: &mir::Lvalue<'tcx>,
    ) -> EvalResult<'tcx> {
        let dest = self.eval_lvalue(lvalue)?;
        let dest_ty = self.lvalue_ty(lvalue);
        let dest_layout = self.type_layout(dest_ty)?;

        use rustc::mir::Rvalue::*;
        match *rvalue {
            Use(ref operand) => {
                let value = self.eval_operand(operand)?;
                self.write_value(value, dest, dest_ty)?;
            }

            BinaryOp(bin_op, ref left, ref right) => {
                if self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)? {
                    // There was an overflow in an unchecked binop.  Right now, we consider this an error and bail out.
                    // The rationale is that the reason rustc emits unchecked binops in release mode (vs. the checked binops
                    // it emits in debug mode) is performance, but it doesn't cost us any performance in miri.
                    // If, however, the compiler ever starts transforming unchecked intrinsics into unchecked binops,
                    // we have to go back to just ignoring the overflow here.
                    return Err(EvalError::OverflowingMath);
                }
            }

            CheckedBinaryOp(bin_op, ref left, ref right) => {
                self.intrinsic_with_overflow(bin_op, left, right, dest, dest_ty)?;
            }

            UnaryOp(un_op, ref operand) => {
                let val = self.eval_operand_to_primval(operand)?;
                let kind = self.ty_to_primval_kind(dest_ty)?;
                self.write_primval(dest, operator::unary_op(un_op, val, kind)?, dest_ty)?;
            }

            // Skip everything for zsts
            Aggregate(..) if self.type_size(dest_ty)? == Some(0) => {}

            Aggregate(ref kind, ref operands) => {
                self.inc_step_counter_and_check_limit(operands.len() as u64)?;
                use rustc::ty::layout::Layout::*;
                match *dest_layout {
                    Univariant { ref variant, .. } => {
                        if variant.packed {
                            let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
                            self.memory.mark_packed(ptr, variant.stride().bytes());
                        }
                        self.assign_fields(dest, dest_ty, operands)?;
                    }

                    Array { .. } => {
                        self.assign_fields(dest, dest_ty, operands)?;
                    }

                    General { discr, ref variants, .. } => {
                        if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind {
                            let discr_val = adt_def.discriminants(self.tcx)
                                .nth(variant)
                                .expect("broken mir: Adt variant id invalid")
                                .to_u128_unchecked();
                            let discr_size = discr.size().bytes();
                            if variants[variant].packed {
                                let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
                                self.memory.mark_packed(ptr, variants[variant].stride().bytes());
                            }

                            self.assign_discr_and_fields(
                                dest,
                                dest_ty,
                                variants[variant].offsets[0].bytes(),
                                operands,
                                discr_val,
                                variant,
                                discr_size,
                            )?;
                        } else {
                            bug!("tried to assign {:?} to Layout::General", kind);
                        }
                    }

                    RawNullablePointer { nndiscr, .. } => {
                        if let mir::AggregateKind::Adt(_, variant, _, _) = **kind {
                            if nndiscr == variant as u64 {
                                assert_eq!(operands.len(), 1);
                                let operand = &operands[0];
                                let value = self.eval_operand(operand)?;
                                let value_ty = self.operand_ty(operand);
                                self.write_value(value, dest, value_ty)?;
                            } else {
                                if let Some(operand) = operands.get(0) {
                                    assert_eq!(operands.len(), 1);
                                    let operand_ty = self.operand_ty(operand);
                                    assert_eq!(self.type_size(operand_ty)?, Some(0));
                                }
                                self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
                            }
                        } else {
                            bug!("tried to assign {:?} to Layout::RawNullablePointer", kind);
                        }
                    }

                    StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => {
                        if let mir::AggregateKind::Adt(_, variant, _, _) = **kind {
                            if nonnull.packed {
                                let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
                                self.memory.mark_packed(ptr, nonnull.stride().bytes());
                            }
                            if nndiscr == variant as u64 {
                                self.assign_fields(dest, dest_ty, operands)?;
                            } else {
                                for operand in operands {
                                    let operand_ty = self.operand_ty(operand);
                                    assert_eq!(self.type_size(operand_ty)?, Some(0));
                                }
                                let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;

                                // FIXME(solson)
                                let dest = self.force_allocation(dest)?.to_ptr()?;

                                let dest = dest.offset(offset.bytes(), self.memory.layout)?;
                                let dest_size = self.type_size(ty)?
                                    .expect("bad StructWrappedNullablePointer discrfield");
                                self.memory.write_int(dest, 0, dest_size)?;
                            }
                        } else {
                            bug!("tried to assign {:?} to Layout::RawNullablePointer", kind);
                        }
                    }

                    CEnum { .. } => {
                        assert_eq!(operands.len(), 0);
                        if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind {
                            let n = adt_def.discriminants(self.tcx)
                                .nth(variant)
                                .expect("broken mir: Adt variant index invalid")
                                .to_u128_unchecked();
                            self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?;
                        } else {
                            bug!("tried to assign {:?} to Layout::CEnum", kind);
                        }
                    }

                    Vector { count, .. } => {
                        debug_assert_eq!(count, operands.len() as u64);
                        self.assign_fields(dest, dest_ty, operands)?;
                    }

                    UntaggedUnion { .. } => {
                        assert_eq!(operands.len(), 1);
                        let operand = &operands[0];
                        let value = self.eval_operand(operand)?;
                        let value_ty = self.operand_ty(operand);
                        self.write_value(value, dest, value_ty)?;
                    }

                    _ => {
                        return Err(EvalError::Unimplemented(format!(
                            "can't handle destination layout {:?} when assigning {:?}",
                            dest_layout,
                            kind
                        )));
                    }
                }
            }

            Repeat(ref operand, _) => {
                let (elem_ty, length) = match dest_ty.sty {
                    ty::TyArray(elem_ty, n) => (elem_ty, n as u64),
                    _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty),
                };
                self.inc_step_counter_and_check_limit(length)?;
                let elem_size = self.type_size(elem_ty)?
                    .expect("repeat element type must be sized");
                let value = self.eval_operand(operand)?;

                // FIXME(solson)
                let dest = PrimVal::Ptr(self.force_allocation(dest)?.to_ptr()?);

                for i in 0..length {
                    let elem_dest = dest.offset(i * elem_size, self.memory.layout)?;
                    self.write_value_to_ptr(value, elem_dest, elem_ty)?;
                }
            }

            Len(ref lvalue) => {
                let src = self.eval_lvalue(lvalue)?;
                let ty = self.lvalue_ty(lvalue);
                let (_, len) = src.elem_ty_and_len(ty);
                self.write_primval(dest, PrimVal::from_u128(len as u128), dest_ty)?;
            }

            Ref(_, _, ref lvalue) => {
                let src = self.eval_lvalue(lvalue)?;
                let (ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra();
                let ty = self.lvalue_ty(lvalue);

                let val = match extra {
                    LvalueExtra::None => Value::ByVal(ptr),
                    LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_u128(len as u128)),
                    LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Ptr(vtable)),
                    LvalueExtra::DowncastVariant(..) =>
                        bug!("attempted to take a reference to an enum downcast lvalue"),
                };

                // Check alignment and non-NULLness.
                let (_, align) = self.size_and_align_of_dst(ty, val)?;
                match ptr {
                    PrimVal::Ptr(ptr) => {
                        self.memory.check_align(ptr, align, 0)?;
                    }
                    PrimVal::Bytes(bytes) => {
                        let v = ((bytes as u128) % (1 << self.memory.pointer_size())) as u64;
                        if v == 0 {
                            return Err(EvalError::InvalidNullPointerUsage);
                        }
                        if v % align != 0 {
                            return Err(EvalError::AlignmentCheckFailed {
                                has: v % align,
                                required: align,
                            });
                        }
                    }
                    PrimVal::Undef => {
                        return Err(EvalError::ReadUndefBytes);
                    }
                }

{'fraction_non_alphanumeric': 0.13251261007095835, 'fraction_numerical': 0.004479781140463367, 'mean_word_length': 1.5289056081636183, 'pattern_counts': {'":': 0, '<': 175, '<?xml version=': 0, '>': 335, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 44, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6301853', 'n_tokens_mistral': 16573, 'n_tokens_neox': 16231, 'n_words': 3722}