<commit_before>use rustc::mir::repr as mir;

use error::{EvalError, EvalResult};
use memory::Pointer;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PrimVal {
    Bool(bool),
    I8(i8), I16(i16), I32(i32), I64(i64),
    U8(u8), U16(u16), U32(u32), U64(u64),

    AbstractPtr(Pointer),
    FnPtr(Pointer),
    IntegerPtr(u64),
    Char(char),

    F32(f32), F64(f64),
}

/// returns the result of the operation and whether the operation overflowed
pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, (PrimVal, bool)> {
    use rustc::mir::repr::BinOp::*;
    use self::PrimVal::*;

    macro_rules! overflow {
        ($v:ident, $v2:ident, $l:ident, $op:ident, $r:ident) => ({
            let (val, of) = $l.$op($r);
            if of {
                return Ok(($v(val), true));
            } else {
                $v(val)
            }
        })
    }

    macro_rules! int_binops {
        ($v:ident, $l:ident, $r:ident) => ({
            match bin_op {
                Add    => overflow!($v, $v, $l, overflowing_add, $r),
                Sub    => overflow!($v, $v, $l, overflowing_sub, $r),
                Mul    => overflow!($v, $v, $l, overflowing_mul, $r),
                Div    => overflow!($v, $v, $l, overflowing_div, $r),
                Rem    => overflow!($v, $v, $l, overflowing_rem, $r),
                BitXor => $v($l ^ $r),
                BitAnd => $v($l & $r),
                BitOr  => $v($l | $r),

                // these have already been handled
                Shl => unreachable!(),
                Shr => unreachable!(),

                Eq => Bool($l == $r),
                Ne => Bool($l != $r),
                Lt => Bool($l < $r),
                Le => Bool($l <= $r),
                Gt => Bool($l > $r),
                Ge => Bool($l >= $r),
            }
        })
    }

    macro_rules! float_binops {
        ($v:ident, $l:ident, $r:ident) => ({
            match bin_op {
                Add    => $v($l + $r),
                Sub    => $v($l - $r),
                Mul    => $v($l * $r),
                Div    => $v($l / $r),
                Rem    => $v($l % $r),

                // invalid float ops
                BitXor => unreachable!(),
                BitAnd => unreachable!(),
                BitOr  => unreachable!(),
                Shl => unreachable!(),
                Shr => unreachable!(),

                // directly comparing floats is questionable
                // miri could forbid it, or at least miri as rust const eval should forbid it
                Eq => Bool($l == $r),
                Ne => Bool($l != $r),
                Lt => Bool($l < $r),
                Le => Bool($l <= $r),
                Gt => Bool($l > $r),
                Ge => Bool($l >= $r),
            }
        })
    }

    fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> {
        use rustc::mir::repr::BinOp::*;
        match bin_op {
            Eq => Ok(Bool(false)),
            Ne => Ok(Bool(true)),
            Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath),
            _ => unimplemented!(),
        }
    }

    match bin_op {
        // can have rhs with a different numeric type
        Shl | Shr => {
            // these numbers are the maximum number a bitshift rhs could possibly have
            // e.g. u16 can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in that range
            let type_bits: u32 = match left {
                I8(_) | U8(_) => 8,
                I16(_) | U16(_) => 16,
                I32(_) | U32(_) => 32,
                I64(_) | U64(_) => 64,
                _ => unreachable!(),
            };
            assert!(type_bits.is_power_of_two());
            // turn into `u32` because `overflowing_sh{l,r}` only take `u32`
            let r = match right {
                I8(i) => i as u32,
                I16(i) => i as u32,
                I32(i) => i as u32,
                I64(i) => i as u32,
                U8(i) => i as u32,
                U16(i) => i as u32,
                U32(i) => i as u32,
                U64(i) => i as u32,
                _ => panic!("bad MIR: bitshift rhs is not integral"),
            };
            // apply mask
            let r = r & (type_bits - 1);
            macro_rules! shift {
                ($v:ident, $l:ident, $r:ident) => ({
                    match bin_op {
                        Shl => overflow!($v, U32, $l, overflowing_shl, $r),
                        Shr => overflow!($v, U32, $l, overflowing_shr, $r),
                        _ => unreachable!(),
                    }
                })
            }
            let val = match left {
                I8(l) => shift!(I8, l, r),
                I16(l) => shift!(I16, l, r),
                I32(l) => shift!(I32, l, r),
                I64(l) => shift!(I64, l, r),
                U8(l) => shift!(U8, l, r),
                U16(l) => shift!(U16, l, r),
                U32(l) => shift!(U32, l, r),
                U64(l) => shift!(U64, l, r),
                _ => unreachable!(),
            };
            return Ok((val, false));
        },
        _ => {},
    }

    let val = match (left, right) {
        (I8(l),  I8(r))  => int_binops!(I8, l, r),
        (I16(l), I16(r)) => int_binops!(I16, l, r),
        (I32(l), I32(r)) => int_binops!(I32, l, r),
        (I64(l), I64(r)) => int_binops!(I64, l, r),
        (U8(l),  U8(r))  => int_binops!(U8, l, r),
        (U16(l), U16(r)) => int_binops!(U16, l, r),
        (U32(l), U32(r)) => int_binops!(U32, l, r),
        (U64(l), U64(r)) => int_binops!(U64, l, r),
        (F32(l), F32(r)) => float_binops!(F32, l, r),
        (F64(l), F64(r)) => float_binops!(F64, l, r),
        (Char(l), Char(r)) => match bin_op {
            Eq => Bool(l == r),
            Ne => Bool(l != r),
            Lt => Bool(l < r),
            Le => Bool(l <= r),
            Gt => Bool(l > r),
            Ge => Bool(l >= r),
            _ => panic!("invalid char op: {:?}", bin_op),
        },

        (Bool(l), Bool(r)) => {
            Bool(match bin_op {
                Eq => l == r,
                Ne => l != r,
                Lt => l < r,
                Le => l <= r,
                Gt => l > r,
                Ge => l >= r,
                BitOr => l | r,
                BitXor => l ^ r,
                BitAnd => l & r,
                Add | Sub | Mul | Div | Rem | Shl | Shr => return Err(EvalError::InvalidBoolOp(bin_op)),
            })
        }

        (IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r),

        (AbstractPtr(_), IntegerPtr(_)) |
        (IntegerPtr(_), AbstractPtr(_)) |
        (FnPtr(_), AbstractPtr(_)) |
        (AbstractPtr(_), FnPtr(_)) |
        (FnPtr(_), IntegerPtr(_)) |
        (IntegerPtr(_), FnPtr(_)) =>
            unrelated_ptr_ops(bin_op)?,

        (FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op {
            Eq => Bool(l_ptr == r_ptr),
            Ne => Bool(l_ptr != r_ptr),
            _ => return Err(EvalError::Unimplemented(format!("unimplemented fn ptr comparison: {:?}", bin_op))),
        },

        (AbstractPtr(l_ptr), AbstractPtr(r_ptr)) => {
            if l_ptr.alloc_id != r_ptr.alloc_id {
                return Ok((unrelated_ptr_ops(bin_op)?, false));
            }

            let l = l_ptr.offset;
            let r = r_ptr.offset;

            match bin_op {
                Eq => Bool(l == r),
                Ne => Bool(l != r),
                Lt => Bool(l < r),
                Le => Bool(l <= r),
                Gt => Bool(l > r),
                Ge => Bool(l >= r),
                _ => return Err(EvalError::Unimplemented(format!("unimplemented ptr op: {:?}", bin_op))),
            }
        }

        (l, r) => return Err(EvalError::Unimplemented(format!("unimplemented binary op: {:?}, {:?}, {:?}", l, r, bin_op))),
    };

    Ok((val, false))
}

pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> {
    use rustc::mir::repr::UnOp::*;
    use self::PrimVal::*;
    match (un_op, val) {
        (Not, Bool(b)) => Ok(Bool(!b)),
        (Not, I8(n))  => Ok(I8(!n)),
        (Neg, I8(n))  => Ok(I8(-n)),
        (Not, I16(n)) => Ok(I16(!n)),
        (Neg, I16(n)) => Ok(I16(-n)),
        (Not, I32(n)) => Ok(I32(!n)),
        (Neg, I32(n)) => Ok(I32(-n)),
        (Not, I64(n)) => Ok(I64(!n)),
        (Neg, I64(n)) => Ok(I64(-n)),
        (Not, U8(n))  => Ok(U8(!n)),
        (Not, U16(n)) => Ok(U16(!n)),
        (Not, U32(n)) => Ok(U32(!n)),
        (Not, U64(n)) => Ok(U64(!n)),

        (Neg, F64(n)) => Ok(F64(-n)),
        (Neg, F32(n)) => Ok(F32(-n)),
        _ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))),
    }
}
<commit_msg>comparing floats is necessary in rare cases<commit_after>use rustc::mir::repr as mir;

use error::{EvalError, EvalResult};
use memory::Pointer;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PrimVal {
    Bool(bool),
    I8(i8), I16(i16), I32(i32), I64(i64),
    U8(u8), U16(u16), U32(u32), U64(u64),

    AbstractPtr(Pointer),
    FnPtr(Pointer),
    IntegerPtr(u64),
    Char(char),

    F32(f32), F64(f64),
}

/// returns the result of the operation and whether the operation overflowed
pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, (PrimVal, bool)> {
    use rustc::mir::repr::BinOp::*;
    use self::PrimVal::*;

    macro_rules! overflow {
        ($v:ident, $v2:ident, $l:ident, $op:ident, $r:ident) => ({
            let (val, of) = $l.$op($r);
            if of {
                return Ok(($v(val), true));
            } else {
                $v(val)
            }
        })
    }

    macro_rules! int_binops {
        ($v:ident, $l:ident, $r:ident) => ({
            match bin_op {
                Add    => overflow!($v, $v, $l, overflowing_add, $r),
                Sub    => overflow!($v, $v, $l, overflowing_sub, $r),
                Mul    => overflow!($v, $v, $l, overflowing_mul, $r),
                Div    => overflow!($v, $v, $l, overflowing_div, $r),
                Rem    => overflow!($v, $v, $l, overflowing_rem, $r),
                BitXor => $v($l ^ $r),
                BitAnd => $v($l & $r),
                BitOr  => $v($l | $r),

                // these have already been handled
                Shl => unreachable!(),
                Shr => unreachable!(),

                Eq => Bool($l == $r),
                Ne => Bool($l != $r),
                Lt => Bool($l < $r),
                Le => Bool($l <= $r),
                Gt => Bool($l > $r),
                Ge => Bool($l >= $r),
            }
        })
    }

    macro_rules! float_binops {
        ($v:ident, $l:ident, $r:ident) => ({
            match bin_op {
                Add    => $v($l + $r),
                Sub    => $v($l - $r),
                Mul    => $v($l * $r),
                Div    => $v($l / $r),
                Rem    => $v($l % $r),

                // invalid float ops
                BitXor => unreachable!(),
                BitAnd => unreachable!(),
                BitOr  => unreachable!(),
                Shl => unreachable!(),
                Shr => unreachable!(),

                Eq => Bool($l == $r),
                Ne => Bool($l != $r),
                Lt => Bool($l < $r),
                Le => Bool($l <= $r),
                Gt => Bool($l > $r),
                Ge => Bool($l >= $r),
            }
        })
    }

    fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> {
        use rustc::mir::repr::BinOp::*;
        match bin_op {
            Eq => Ok(Bool(false)),
            Ne => Ok(Bool(true)),
            Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath),
            _ => unimplemented!(),
        }
    }

    match bin_op {
        // can have rhs with a different numeric type
        Shl | Shr => {
            // these numbers are the maximum number a bitshift rhs could possibly have
            // e.g. u16 can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in that range
            let type_bits: u32 = match left {
                I8(_) | U8(_) => 8,
                I16(_) | U16(_) => 16,
                I32(_) | U32(_) => 32,
                I64(_) | U64(_) => 64,
                _ => unreachable!(),
            };
            assert!(type_bits.is_power_of_two());
            // turn into `u32` because `overflowing_sh{l,r}` only take `u32`
            let r = match right {
                I8(i) => i as u32,
                I16(i) => i as u32,
                I32(i) => i as u32,
                I64(i) => i as u32,
                U8(i) => i as u32,
                U16(i) => i as u32,
                U32(i) => i as u32,
                U64(i) => i as u32,
                _ => panic!("bad MIR: bitshift rhs is not integral"),
            };
            // apply mask
            let r = r & (type_bits - 1);
            macro_rules! shift {
                ($v:ident, $l:ident, $r:ident) => ({
                    match bin_op {
                        Shl => overflow!($v, U32, $l, overflowing_shl, $r),
                        Shr => overflow!($v, U32, $l, overflowing_shr, $r),
                        _ => unreachable!(),
                    }
                })
            }
            let val = match left {
                I8(l) => shift!(I8, l, r),
                I16(l) => shift!(I16, l, r),
                I32(l) => shift!(I32, l, r),
                I64(l) => shift!(I64, l, r),
                U8(l) => shift!(U8, l, r),
                U16(l) => shift!(U16, l, r),
                U32(l) => shift!(U32, l, r),
                U64(l) => shift!(U64, l, r),
                _ => unreachable!(),
            };
            return Ok((val, false));
        },
        _ => {},
    }

    let val = match (left, right) {
        (I8(l),  I8(r))  => int_binops!(I8, l, r),
        (I16(l), I16(r)) => int_binops!(I16, l, r),
        (I32(l), I32(r)) => int_binops!(I32, l, r),
        (I64(l), I64(r)) => int_binops!(I64, l, r),
        (U8(l),  U8(r))  => int_binops!(U8, l, r),
        (U16(l), U16(r)) => int_binops!(U16, l, r),
        (U32(l), U32(r)) => int_binops!(U32, l, r),
        (U64(l), U64(r)) => int_binops!(U64, l, r),
        (F32(l), F32(r)) => float_binops!(F32, l, r),
        (F64(l), F64(r)) => float_binops!(F64, l, r),
        (Char(l), Char(r)) => match bin_op {
            Eq => Bool(l == r),
            Ne => Bool(l != r),
            Lt => Bool(l < r),
            Le => Bool(l <= r),
            Gt => Bool(l > r),
            Ge => Bool(l >= r),
            _ => panic!("invalid char op: {:?}", bin_op),
        },

        (Bool(l), Bool(r)) => {
            Bool(match bin_op {
                Eq => l == r,
                Ne => l != r,
                Lt => l < r,
                Le => l <= r,
                Gt => l > r,
                Ge => l >= r,
                BitOr => l | r,
                BitXor => l ^ r,
                BitAnd => l & r,
                Add | Sub | Mul | Div | Rem | Shl | Shr => return Err(EvalError::InvalidBoolOp(bin_op)),
            })
        }

        (IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r),

        (AbstractPtr(_), IntegerPtr(_)) |
        (IntegerPtr(_), AbstractPtr(_)) |
        (FnPtr(_), AbstractPtr(_)) |
        (AbstractPtr(_), FnPtr(_)) |
        (FnPtr(_), IntegerPtr(_)) |
        (IntegerPtr(_), FnPtr(_)) =>
            unrelated_ptr_ops(bin_op)?,

        (FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op {
            Eq => Bool(l_ptr == r_ptr),
            Ne => Bool(l_ptr != r_ptr),
            _ => return Err(EvalError::Unimplemented(format!("unimplemented fn ptr comparison: {:?}", bin_op))),
        },

        (AbstractPtr(l_ptr), AbstractPtr(r_ptr)) => {
            if l_ptr.alloc_id != r_ptr.alloc_id {
                return Ok((unrelated_ptr_ops(bin_op)?, false));
            }

            let l = l_ptr.offset;
            let r = r_ptr.offset;

            match bin_op {
                Eq => Bool(l == r),
                Ne => Bool(l != r),
                Lt => Bool(l < r),
                Le => Bool(l <= r),
                Gt => Bool(l > r),
                Ge => Bool(l >= r),
                _ => return Err(EvalError::Unimplemented(format!("unimplemented ptr op: {:?}", bin_op))),
            }
        }

        (l, r) => return Err(EvalError::Unimplemented(format!("unimplemented binary op: {:?}, {:?}, {:?}", l, r, bin_op))),
    };

    Ok((val, false))
}

pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> {
    use rustc::mir::repr::UnOp::*;
    use self::PrimVal::*;
    match (un_op, val) {
        (Not, Bool(b)) => Ok(Bool(!b)),
        (Not, I8(n))  => Ok(I8(!n)),
        (Neg, I8(n))  => Ok(I8(-n)),
        (Not, I16(n)) => Ok(I16(!n)),
        (Neg, I16(n)) => Ok(I16(-n)),
        (Not, I32(n)) => Ok(I32(!n)),
        (Neg, I32(n)) => Ok(I32(-n)),
        (Not, I64(n)) => Ok(I64(!n)),
        (Neg, I64(n)) => Ok(I64(-n)),
        (Not, U8(n))  => Ok(U8(!n)),
        (Not, U16(n)) => Ok(U16(!n)),
        (Not, U32(n)) => Ok(U32(!n)),
        (Not, U64(n)) => Ok(U64(!n)),

        (Neg, F64(n)) => Ok(F64(-n)),
        (Neg, F32(n)) => Ok(F32(-n)),
        _ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))),
    }
}

{'fraction_non_alphanumeric': 0.19049532227515353, 'fraction_numerical': 0.02823853526947139, 'mean_word_length': 1.433179723502304, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 297, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7411207', 'n_tokens_mistral': 6472, 'n_tokens_neox': 5980, 'n_words': 1368}