<commit_before>use rustc::mir;
use rustc::ty::{self, layout::TyLayout};
use syntax::ast::FloatTy;
use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::Float;
use rustc::mir::interpret::{InterpResult, Scalar};

use super::{InterpretCx, PlaceTy, Immediate, Machine, ImmTy};

impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
    /// Applies the binary operation `op` to the two operands and writes a tuple of the result
    /// and a boolean signifying the potential overflow to the destination.
    pub fn binop_with_overflow(
        &mut self,
        op: mir::BinOp,
        left: ImmTy<'tcx, M::PointerTag>,
        right: ImmTy<'tcx, M::PointerTag>,
        dest: PlaceTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx> {
        let (val, overflowed) = self.binary_op(op, left, right)?;
        let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into());
        self.write_immediate(val, dest)
    }

    /// Applies the binary operation `op` to the arguments and writes the result to the
    /// destination.
    pub fn binop_ignore_overflow(
        &mut self,
        op: mir::BinOp,
        left: ImmTy<'tcx, M::PointerTag>,
        right: ImmTy<'tcx, M::PointerTag>,
        dest: PlaceTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx> {
        let (val, _overflowed) = self.binary_op(op, left, right)?;
        self.write_scalar(val, dest)
    }
}

impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
    fn binary_char_op(
        &self,
        bin_op: mir::BinOp,
        l: char,
        r: char,
    ) -> (Scalar<M::PointerTag>, bool) {
        use rustc::mir::BinOp::*;

        let res = match bin_op {
            Eq => l == r,
            Ne => l != r,
            Lt => l < r,
            Le => l <= r,
            Gt => l > r,
            Ge => l >= r,
            _ => bug!("Invalid operation on char: {:?}", bin_op),
        };
        return (Scalar::from_bool(res), false);
    }

    fn binary_bool_op(
        &self,
        bin_op: mir::BinOp,
        l: bool,
        r: bool,
    ) -> (Scalar<M::PointerTag>, bool) {
        use rustc::mir::BinOp::*;

        let res = match bin_op {
            Eq => l == r,
            Ne => l != r,
            Lt => l < r,
            Le => l <= r,
            Gt => l > r,
            Ge => l >= r,
            BitAnd => l & r,
            BitOr => l | r,
            BitXor => l ^ r,
            _ => bug!("Invalid operation on bool: {:?}", bin_op),
        };
        return (Scalar::from_bool(res), false);
    }

    fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>(
        &self,
        bin_op: mir::BinOp,
        l: F,
        r: F,
    ) -> (Scalar<M::PointerTag>, bool) {
        use rustc::mir::BinOp::*;

        let val = match bin_op {
            Eq => Scalar::from_bool(l == r),
            Ne => Scalar::from_bool(l != r),
            Lt => Scalar::from_bool(l < r),
            Le => Scalar::from_bool(l <= r),
            Gt => Scalar::from_bool(l > r),
            Ge => Scalar::from_bool(l >= r),
            Add => (l + r).value.into(),
            Sub => (l - r).value.into(),
            Mul => (l * r).value.into(),
            Div => (l / r).value.into(),
            Rem => (l % r).value.into(),
            _ => bug!("invalid float op: `{:?}`", bin_op),
        };
        return (val, false);
    }

    fn binary_int_op(
        &self,
        bin_op: mir::BinOp,
        // passing in raw bits
        l: u128,
        left_layout: TyLayout<'tcx>,
        r: u128,
        right_layout: TyLayout<'tcx>,
    ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
        use rustc::mir::BinOp::*;

        // Shift ops can have an RHS with a different numeric type.
        if bin_op == Shl || bin_op == Shr {
            let signed = left_layout.abi.is_signed();
            let mut oflo = (r as u32 as u128) != r;
            let mut r = r as u32;
            let size = left_layout.size;
            oflo |= r >= size.bits() as u32;
            if oflo {
                r %= size.bits() as u32;
            }
            let result = if signed {
                let l = self.sign_extend(l, left_layout) as i128;
                let result = match bin_op {
                    Shl => l << r,
                    Shr => l >> r,
                    _ => bug!("it has already been checked that this is a shift op"),
                };
                result as u128
            } else {
                match bin_op {
                    Shl => l << r,
                    Shr => l >> r,
                    _ => bug!("it has already been checked that this is a shift op"),
                }
            };
            let truncated = self.truncate(result, left_layout);
            return Ok((Scalar::from_uint(truncated, size), oflo));
        }

        // For the remaining ops, the types must be the same on both sides
        if left_layout.ty != right_layout.ty {
            let msg = format!(
                "unimplemented asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
                bin_op,
                l,
                left_layout.ty,
                r,
                right_layout.ty
            );
            return err!(Unimplemented(msg));
        }

        // Operations that need special treatment for signed integers
        if left_layout.abi.is_signed() {
            let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
                Lt => Some(i128::lt),
                Le => Some(i128::le),
                Gt => Some(i128::gt),
                Ge => Some(i128::ge),
                _ => None,
            };
            if let Some(op) = op {
                let l = self.sign_extend(l, left_layout) as i128;
                let r = self.sign_extend(r, right_layout) as i128;
                return Ok((Scalar::from_bool(op(&l, &r)), false));
            }
            let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
                Div if r == 0 => return err!(DivisionByZero),
                Rem if r == 0 => return err!(RemainderByZero),
                Div => Some(i128::overflowing_div),
                Rem => Some(i128::overflowing_rem),
                Add => Some(i128::overflowing_add),
                Sub => Some(i128::overflowing_sub),
                Mul => Some(i128::overflowing_mul),
                _ => None,
            };
            if let Some(op) = op {
                let l128 = self.sign_extend(l, left_layout) as i128;
                let r = self.sign_extend(r, right_layout) as i128;
                let size = left_layout.size;
                match bin_op {
                    Rem | Div => {
                        // int_min / -1
                        if r == -1 && l == (1 << (size.bits() - 1)) {
                            return Ok((Scalar::from_uint(l, size), true));
                        }
                    },
                    _ => {},
                }
                trace!("{}, {}, {}", l, l128, r);
                let (result, mut oflo) = op(l128, r);
                trace!("{}, {}", result, oflo);
                if !oflo && size.bits() != 128 {
                    let max = 1 << (size.bits() - 1);
                    oflo = result >= max || result < -max;
                }
                // this may be out-of-bounds for the result type, so we have to truncate ourselves
                let result = result as u128;
                let truncated = self.truncate(result, left_layout);
                return Ok((Scalar::from_uint(truncated, size), oflo));
            }
        }

        let size = left_layout.size;

        // only ints left
        let val = match bin_op {
            Eq => Scalar::from_bool(l == r),
            Ne => Scalar::from_bool(l != r),

            Lt => Scalar::from_bool(l < r),
            Le => Scalar::from_bool(l <= r),
            Gt => Scalar::from_bool(l > r),
            Ge => Scalar::from_bool(l >= r),

            BitOr => Scalar::from_uint(l | r, size),
            BitAnd => Scalar::from_uint(l & r, size),
            BitXor => Scalar::from_uint(l ^ r, size),

            Add | Sub | Mul | Rem | Div => {
                debug_assert!(!left_layout.abi.is_signed());
                let op: fn(u128, u128) -> (u128, bool) = match bin_op {
                    Add => u128::overflowing_add,
                    Sub => u128::overflowing_sub,
                    Mul => u128::overflowing_mul,
                    Div if r == 0 => return err!(DivisionByZero),
                    Rem if r == 0 => return err!(RemainderByZero),
                    Div => u128::overflowing_div,
                    Rem => u128::overflowing_rem,
                    _ => bug!(),
                };
                let (result, oflo) = op(l, r);
                let truncated = self.truncate(result, left_layout);
                return Ok((Scalar::from_uint(truncated, size), oflo || truncated != result));
            }

            _ => {
                let msg = format!(
                    "unimplemented binary op {:?}: {:?}, {:?} (both {:?})",
                    bin_op,
                    l,
                    r,
                    right_layout.ty,
                );
                return err!(Unimplemented(msg));
            }
        };

        Ok((val, false))
    }

    /// Returns the result of the specified operation and whether it overflowed.
    #[inline]
    pub fn binary_op(
        &self,
        bin_op: mir::BinOp,
        left: ImmTy<'tcx, M::PointerTag>,
        right: ImmTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
        trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
            bin_op, *left, left.layout.ty, *right, right.layout.ty);

        match left.layout.ty.sty {
            ty::Char => {
                assert_eq!(left.layout.ty, right.layout.ty);
                let left = left.to_scalar()?;
                let right = right.to_scalar()?;
                Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?))
            }
            ty::Bool => {
                assert_eq!(left.layout.ty, right.layout.ty);
                let left = left.to_scalar()?;
                let right = right.to_scalar()?;
                Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?))
            }
            ty::Float(fty) => {
                assert_eq!(left.layout.ty, right.layout.ty);
                let left = left.to_scalar()?;
                let right = right.to_scalar()?;
                Ok(match fty {
                    FloatTy::F32 => self.binary_float_op(bin_op, left.to_f32()?, right.to_f32()?),
                    FloatTy::F64 => self.binary_float_op(bin_op, left.to_f64()?, right.to_f64()?),
                })
            }
            _ => {
                // Must be integer(-like) types.  Don't forget about == on fn pointers.
                assert!(left.layout.ty.is_integral() || left.layout.ty.is_unsafe_ptr() ||
                    left.layout.ty.is_fn());
                assert!(right.layout.ty.is_integral() || right.layout.ty.is_unsafe_ptr() ||
                    right.layout.ty.is_fn());

                // Handle operations that support pointer values
                if left.to_scalar_ptr()?.is_ptr() ||
                    right.to_scalar_ptr()?.is_ptr() ||
                    bin_op == mir::BinOp::Offset
                {
                    return M::ptr_op(self, bin_op, left, right);
                }

                // Everything else only works with "proper" bits
                let l = left.to_bits().expect("we checked is_ptr");
                let r = right.to_bits().expect("we checked is_ptr");
                self.binary_int_op(bin_op, l, left.layout, r, right.layout)
            }
        }
    }

    pub fn unary_op(
        &self,
        un_op: mir::UnOp,
        val: ImmTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
        use rustc::mir::UnOp::*;

        let layout = val.layout;
        let val = val.to_scalar()?;
        trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty);

        match layout.ty.sty {
            ty::Bool => {
                let val = val.to_bool()?;
                let res = match un_op {
                    Not => !val,
                    _ => bug!("Invalid bool op {:?}", un_op)
                };
                Ok(Scalar::from_bool(res))
            }
            ty::Float(fty) => {
                let val = val.to_bits(layout.size)?;
                let res = match (un_op, fty) {
                    (Neg, FloatTy::F32) => Single::to_bits(-Single::from_bits(val)),
                    (Neg, FloatTy::F64) => Double::to_bits(-Double::from_bits(val)),
                    _ => bug!("Invalid float op {:?}", un_op)
                };
                Ok(Scalar::from_uint(res, layout.size))
            }
            _ => {
                assert!(layout.ty.is_integral());
                let val = val.to_bits(layout.size)?;
                let res = match un_op {
                    Not => !val,
                    Neg => {
                        assert!(layout.abi.is_signed());
                        (-(val as i128)) as u128
                    }
                };
                // res needs tuncating
                Ok(Scalar::from_uint(self.truncate(res, layout), layout.size))
            }
        }
    }
}
<commit_msg>make unary float negation slightly nicer<commit_after>use rustc::mir;
use rustc::ty::{self, layout::TyLayout};
use syntax::ast::FloatTy;
use rustc_apfloat::Float;
use rustc::mir::interpret::{InterpResult, Scalar};

use super::{InterpretCx, PlaceTy, Immediate, Machine, ImmTy};

impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
    /// Applies the binary operation `op` to the two operands and writes a tuple of the result
    /// and a boolean signifying the potential overflow to the destination.
    pub fn binop_with_overflow(
        &mut self,
        op: mir::BinOp,
        left: ImmTy<'tcx, M::PointerTag>,
        right: ImmTy<'tcx, M::PointerTag>,
        dest: PlaceTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx> {
        let (val, overflowed) = self.binary_op(op, left, right)?;
        let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into());
        self.write_immediate(val, dest)
    }

    /// Applies the binary operation `op` to the arguments and writes the result to the
    /// destination.
    pub fn binop_ignore_overflow(
        &mut self,
        op: mir::BinOp,
        left: ImmTy<'tcx, M::PointerTag>,
        right: ImmTy<'tcx, M::PointerTag>,
        dest: PlaceTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx> {
        let (val, _overflowed) = self.binary_op(op, left, right)?;
        self.write_scalar(val, dest)
    }
}

impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
    fn binary_char_op(
        &self,
        bin_op: mir::BinOp,
        l: char,
        r: char,
    ) -> (Scalar<M::PointerTag>, bool) {
        use rustc::mir::BinOp::*;

        let res = match bin_op {
            Eq => l == r,
            Ne => l != r,
            Lt => l < r,
            Le => l <= r,
            Gt => l > r,
            Ge => l >= r,
            _ => bug!("Invalid operation on char: {:?}", bin_op),
        };
        return (Scalar::from_bool(res), false);
    }

    fn binary_bool_op(
        &self,
        bin_op: mir::BinOp,
        l: bool,
        r: bool,
    ) -> (Scalar<M::PointerTag>, bool) {
        use rustc::mir::BinOp::*;

        let res = match bin_op {
            Eq => l == r,
            Ne => l != r,
            Lt => l < r,
            Le => l <= r,
            Gt => l > r,
            Ge => l >= r,
            BitAnd => l & r,
            BitOr => l | r,
            BitXor => l ^ r,
            _ => bug!("Invalid operation on bool: {:?}", bin_op),
        };
        return (Scalar::from_bool(res), false);
    }

    fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>(
        &self,
        bin_op: mir::BinOp,
        l: F,
        r: F,
    ) -> (Scalar<M::PointerTag>, bool) {
        use rustc::mir::BinOp::*;

        let val = match bin_op {
            Eq => Scalar::from_bool(l == r),
            Ne => Scalar::from_bool(l != r),
            Lt => Scalar::from_bool(l < r),
            Le => Scalar::from_bool(l <= r),
            Gt => Scalar::from_bool(l > r),
            Ge => Scalar::from_bool(l >= r),
            Add => (l + r).value.into(),
            Sub => (l - r).value.into(),
            Mul => (l * r).value.into(),
            Div => (l / r).value.into(),
            Rem => (l % r).value.into(),
            _ => bug!("invalid float op: `{:?}`", bin_op),
        };
        return (val, false);
    }

    fn binary_int_op(
        &self,
        bin_op: mir::BinOp,
        // passing in raw bits
        l: u128,
        left_layout: TyLayout<'tcx>,
        r: u128,
        right_layout: TyLayout<'tcx>,
    ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
        use rustc::mir::BinOp::*;

        // Shift ops can have an RHS with a different numeric type.
        if bin_op == Shl || bin_op == Shr {
            let signed = left_layout.abi.is_signed();
            let mut oflo = (r as u32 as u128) != r;
            let mut r = r as u32;
            let size = left_layout.size;
            oflo |= r >= size.bits() as u32;
            if oflo {
                r %= size.bits() as u32;
            }
            let result = if signed {
                let l = self.sign_extend(l, left_layout) as i128;
                let result = match bin_op {
                    Shl => l << r,
                    Shr => l >> r,
                    _ => bug!("it has already been checked that this is a shift op"),
                };
                result as u128
            } else {
                match bin_op {
                    Shl => l << r,
                    Shr => l >> r,
                    _ => bug!("it has already been checked that this is a shift op"),
                }
            };
            let truncated = self.truncate(result, left_layout);
            return Ok((Scalar::from_uint(truncated, size), oflo));
        }

        // For the remaining ops, the types must be the same on both sides
        if left_layout.ty != right_layout.ty {
            let msg = format!(
                "unimplemented asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
                bin_op,
                l,
                left_layout.ty,
                r,
                right_layout.ty
            );
            return err!(Unimplemented(msg));
        }

        // Operations that need special treatment for signed integers
        if left_layout.abi.is_signed() {
            let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
                Lt => Some(i128::lt),
                Le => Some(i128::le),
                Gt => Some(i128::gt),
                Ge => Some(i128::ge),
                _ => None,
            };
            if let Some(op) = op {
                let l = self.sign_extend(l, left_layout) as i128;
                let r = self.sign_extend(r, right_layout) as i128;
                return Ok((Scalar::from_bool(op(&l, &r)), false));
            }
            let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
                Div if r == 0 => return err!(DivisionByZero),
                Rem if r == 0 => return err!(RemainderByZero),
                Div => Some(i128::overflowing_div),
                Rem => Some(i128::overflowing_rem),
                Add => Some(i128::overflowing_add),
                Sub => Some(i128::overflowing_sub),
                Mul => Some(i128::overflowing_mul),
                _ => None,
            };
            if let Some(op) = op {
                let l128 = self.sign_extend(l, left_layout) as i128;
                let r = self.sign_extend(r, right_layout) as i128;
                let size = left_layout.size;
                match bin_op {
                    Rem | Div => {
                        // int_min / -1
                        if r == -1 && l == (1 << (size.bits() - 1)) {
                            return Ok((Scalar::from_uint(l, size), true));
                        }
                    },
                    _ => {},
                }
                trace!("{}, {}, {}", l, l128, r);
                let (result, mut oflo) = op(l128, r);
                trace!("{}, {}", result, oflo);
                if !oflo && size.bits() != 128 {
                    let max = 1 << (size.bits() - 1);
                    oflo = result >= max || result < -max;
                }
                // this may be out-of-bounds for the result type, so we have to truncate ourselves
                let result = result as u128;
                let truncated = self.truncate(result, left_layout);
                return Ok((Scalar::from_uint(truncated, size), oflo));
            }
        }

        let size = left_layout.size;

        // only ints left
        let val = match bin_op {
            Eq => Scalar::from_bool(l == r),
            Ne => Scalar::from_bool(l != r),

            Lt => Scalar::from_bool(l < r),
            Le => Scalar::from_bool(l <= r),
            Gt => Scalar::from_bool(l > r),
            Ge => Scalar::from_bool(l >= r),

            BitOr => Scalar::from_uint(l | r, size),
            BitAnd => Scalar::from_uint(l & r, size),
            BitXor => Scalar::from_uint(l ^ r, size),

            Add | Sub | Mul | Rem | Div => {
                debug_assert!(!left_layout.abi.is_signed());
                let op: fn(u128, u128) -> (u128, bool) = match bin_op {
                    Add => u128::overflowing_add,
                    Sub => u128::overflowing_sub,
                    Mul => u128::overflowing_mul,
                    Div if r == 0 => return err!(DivisionByZero),
                    Rem if r == 0 => return err!(RemainderByZero),
                    Div => u128::overflowing_div,
                    Rem => u128::overflowing_rem,
                    _ => bug!(),
                };
                let (result, oflo) = op(l, r);
                let truncated = self.truncate(result, left_layout);
                return Ok((Scalar::from_uint(truncated, size), oflo || truncated != result));
            }

            _ => {
                let msg = format!(
                    "unimplemented binary op {:?}: {:?}, {:?} (both {:?})",
                    bin_op,
                    l,
                    r,
                    right_layout.ty,
                );
                return err!(Unimplemented(msg));
            }
        };

        Ok((val, false))
    }

    /// Returns the result of the specified operation and whether it overflowed.
    #[inline]
    pub fn binary_op(
        &self,
        bin_op: mir::BinOp,
        left: ImmTy<'tcx, M::PointerTag>,
        right: ImmTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
        trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
            bin_op, *left, left.layout.ty, *right, right.layout.ty);

        match left.layout.ty.sty {
            ty::Char => {
                assert_eq!(left.layout.ty, right.layout.ty);
                let left = left.to_scalar()?;
                let right = right.to_scalar()?;
                Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?))
            }
            ty::Bool => {
                assert_eq!(left.layout.ty, right.layout.ty);
                let left = left.to_scalar()?;
                let right = right.to_scalar()?;
                Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?))
            }
            ty::Float(fty) => {
                assert_eq!(left.layout.ty, right.layout.ty);
                let left = left.to_scalar()?;
                let right = right.to_scalar()?;
                Ok(match fty {
                    FloatTy::F32 => self.binary_float_op(bin_op, left.to_f32()?, right.to_f32()?),
                    FloatTy::F64 => self.binary_float_op(bin_op, left.to_f64()?, right.to_f64()?),
                })
            }
            _ => {
                // Must be integer(-like) types.  Don't forget about == on fn pointers.
                assert!(left.layout.ty.is_integral() || left.layout.ty.is_unsafe_ptr() ||
                    left.layout.ty.is_fn());
                assert!(right.layout.ty.is_integral() || right.layout.ty.is_unsafe_ptr() ||
                    right.layout.ty.is_fn());

                // Handle operations that support pointer values
                if left.to_scalar_ptr()?.is_ptr() ||
                    right.to_scalar_ptr()?.is_ptr() ||
                    bin_op == mir::BinOp::Offset
                {
                    return M::ptr_op(self, bin_op, left, right);
                }

                // Everything else only works with "proper" bits
                let l = left.to_bits().expect("we checked is_ptr");
                let r = right.to_bits().expect("we checked is_ptr");
                self.binary_int_op(bin_op, l, left.layout, r, right.layout)
            }
        }
    }

    pub fn unary_op(
        &self,
        un_op: mir::UnOp,
        val: ImmTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
        use rustc::mir::UnOp::*;

        let layout = val.layout;
        let val = val.to_scalar()?;
        trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty);

        match layout.ty.sty {
            ty::Bool => {
                let val = val.to_bool()?;
                let res = match un_op {
                    Not => !val,
                    _ => bug!("Invalid bool op {:?}", un_op)
                };
                Ok(Scalar::from_bool(res))
            }
            ty::Float(fty) => {
                let res = match (un_op, fty) {
                    (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
                    (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
                    _ => bug!("Invalid float op {:?}", un_op)
                };
                Ok(res)
            }
            _ => {
                assert!(layout.ty.is_integral());
                let val = val.to_bits(layout.size)?;
                let res = match un_op {
                    Not => !val,
                    Neg => {
                        assert!(layout.abi.is_signed());
                        (-(val as i128)) as u128
                    }
                };
                // res needs tuncating
                Ok(Scalar::from_uint(self.truncate(res, layout), layout.size))
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use back::abi;
use llvm::ValueRef;
use rustc::middle::ty::Ty;
use rustc_front::hir;
use rustc_mir::repr as mir;

use trans::asm;
use trans::base;
use trans::build;
use trans::common::{self, Block, Result};
use trans::debuginfo::DebugLoc;
use trans::declare;
use trans::expr;
use trans::machine;
use trans::type_::Type;
use trans::type_of;
use trans::tvec;

use super::MirContext;
use super::operand::OperandRef;

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
    pub fn trans_rvalue(&mut self,
                        bcx: Block<'bcx, 'tcx>,
                        lldest: ValueRef,
                        rvalue: &mir::Rvalue<'tcx>)
                        -> Block<'bcx, 'tcx>
    {
        debug!("trans_rvalue(lldest={}, rvalue={:?})",
               bcx.val_to_string(lldest),
               rvalue);

        match *rvalue {
            mir::Rvalue::Use(ref operand) => {
                self.trans_operand_into(bcx, lldest, operand);
                bcx
            }

            mir::Rvalue::Cast(..) => {
                unimplemented!()
            }

            mir::Rvalue::Repeat(..) => {
                unimplemented!()
            }

            mir::Rvalue::Aggregate(_, ref operands) => {
                for (i, operand) in operands.iter().enumerate() {
                    // Note: perhaps this should be StructGep, but
                    // note that in some cases the values here will
                    // not be structs but arrays.
                    let lldest_i = build::GEPi(bcx, lldest, &[0, i]);
                    self.trans_operand_into(bcx, lldest_i, operand);
                }
                bcx
            }

            mir::Rvalue::Slice { ref input, from_start, from_end } => {
                let ccx = bcx.ccx();
                let input = self.trans_lvalue(bcx, input);
                let (llbase, lllen) = tvec::get_base_and_len(bcx,
                                                             input.llval,
                                                             input.ty.to_ty(bcx.tcx()));
                let llbase1 = build::GEPi(bcx, llbase, &[from_start]);
                let adj = common::C_uint(ccx, from_start + from_end);
                let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None);
                let lladdrdest = expr::get_dataptr(bcx, lldest);
                build::Store(bcx, llbase1, lladdrdest);
                let llmetadest = expr::get_meta(bcx, lldest);
                build::Store(bcx, lllen1, llmetadest);
                bcx
            }

            mir::Rvalue::InlineAsm(inline_asm) => {
                asm::trans_inline_asm(bcx, inline_asm)
            }

            _ => {
                assert!(rvalue_creates_operand(rvalue));
                let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
                build::Store(bcx, temp.llval, lldest);
                bcx
            }
        }
    }

    pub fn trans_rvalue_operand(&mut self,
                                bcx: Block<'bcx, 'tcx>,
                                rvalue: &mir::Rvalue<'tcx>)
                                -> (Block<'bcx, 'tcx>, OperandRef<'tcx>)
    {
        assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);

        match *rvalue {
            mir::Rvalue::Use(ref operand) => {
                let operand = self.trans_operand(bcx, operand);
                (bcx, operand)
            }

            mir::Rvalue::Cast(..) => {
                unimplemented!()
            }

            mir::Rvalue::Ref(_, _, ref lvalue) => {
                let tr_lvalue = self.trans_lvalue(bcx, lvalue);

                // Note: lvalues are indirect, so storing the `llval` into the
                // destination effectively creates a reference.
                (bcx, OperandRef {
                    llval: tr_lvalue.llval,
                    ty: tr_lvalue.ty.to_ty(bcx.tcx()),
                })
            }

            mir::Rvalue::Len(ref lvalue) => {
                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
                let (_, lllen) = tvec::get_base_and_len(bcx,
                                                        tr_lvalue.llval,
                                                        tr_lvalue.ty.to_ty(bcx.tcx()));
                (bcx, OperandRef {
                    llval: lllen,
                    ty: bcx.tcx().types.usize,
                })
            }

            mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
                let lhs = self.trans_operand(bcx, lhs);
                let rhs = self.trans_operand(bcx, rhs);
                let is_float = lhs.ty.is_fp();
                let is_signed = lhs.ty.is_signed();
                let binop_debug_loc = DebugLoc::None;
                let llval = match op {
                    mir::BinOp::Add => if is_float {
                        build::FAdd(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else {
                        build::Add(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    },
                    mir::BinOp::Sub => if is_float {
                        build::FSub(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else {
                        build::Sub(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    },
                    mir::BinOp::Mul => if is_float {
                        build::FMul(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else {
                        build::Mul(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    },
                    mir::BinOp::Div => if is_float {
                        build::FDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else if is_signed {
                        build::SDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else {
                        build::UDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    },
                    mir::BinOp::Rem => if is_float {
                        // LLVM currently always lowers the `frem` instructions appropriate
                        // library calls typically found in libm. Notably f64 gets wired up
                        // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for
                        // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's
                        // instead just an inline function in a header that goes up to a
                        // f64, uses `fmod`, and then comes back down to a f32.
                        //
                        // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will
                        // still unconditionally lower frem instructions over 32-bit floats
                        // to a call to `fmodf`. To work around this we special case MSVC
                        // 32-bit float rem instructions and instead do the call out to
                        // `fmod` ourselves.
                        //
                        // Note that this is currently duplicated with src/libcore/ops.rs
                        // which does the same thing, and it would be nice to perhaps unify
                        // these two implementations one day! Also note that we call `fmod`
                        // for both 32 and 64-bit floats because if we emit any FRem
                        // instruction at all then LLVM is capable of optimizing it into a
                        // 32-bit FRem (which we're trying to avoid).
                        let tcx = bcx.tcx();
                        let use_fmod = tcx.sess.target.target.options.is_like_msvc &&
                            tcx.sess.target.target.arch == "x86";
                        if use_fmod {
                            let f64t = Type::f64(bcx.ccx());
                            let fty = Type::func(&[f64t, f64t], &f64t);
                            let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty,
                                                            tcx.types.f64);
                            if lhs.ty == tcx.types.f32 {
                                let lllhs = build::FPExt(bcx, lhs.llval, f64t);
                                let llrhs = build::FPExt(bcx, rhs.llval, f64t);
                                let llres = build::Call(bcx, llfn, &[lllhs, llrhs],
                                                        None, binop_debug_loc);
                                build::FPTrunc(bcx, llres, Type::f32(bcx.ccx()))
                            } else {
                                build::Call(bcx, llfn, &[lhs.llval, rhs.llval],
                                            None, binop_debug_loc)
                            }
                        } else {
                            build::FRem(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                        }
                    } else if is_signed {
                        build::SRem(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else {
                        build::URem(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    },
                    mir::BinOp::BitOr => build::Or(bcx, lhs.llval, rhs.llval, binop_debug_loc),
                    mir::BinOp::BitAnd => build::And(bcx, lhs.llval, rhs.llval, binop_debug_loc),
                    mir::BinOp::BitXor => build::Xor(bcx, lhs.llval, rhs.llval, binop_debug_loc),
                    mir::BinOp::Shl => common::build_unchecked_lshift(bcx,
                                                                      lhs.llval,
                                                                      rhs.llval,
                                                                      binop_debug_loc),
                    mir::BinOp::Shr => common::build_unchecked_rshift(bcx,
                                                                      lhs.ty,
                                                                      lhs.llval,
                                                                      rhs.llval,
                                                                      binop_debug_loc),
                    mir::BinOp::Eq => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiEq, binop_debug_loc),
                    mir::BinOp::Lt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiLt, binop_debug_loc),
                    mir::BinOp::Le => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiLe, binop_debug_loc),
                    mir::BinOp::Ne => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiNe, binop_debug_loc),
                    mir::BinOp::Ge => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiGe, binop_debug_loc),
                    mir::BinOp::Gt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiGt, binop_debug_loc),
                };
                (bcx, OperandRef {
                    llval: llval,
                    ty: lhs.ty,
                })
            }

            mir::Rvalue::UnaryOp(op, ref operand) => {
                let operand = self.trans_operand(bcx, operand);
                let is_float = operand.ty.is_fp();
                let debug_loc = DebugLoc::None;
                let llval = match op {
                    mir::UnOp::Not => build::Not(bcx, operand.llval, debug_loc),
                    mir::UnOp::Neg => if is_float {
                        build::FNeg(bcx, operand.llval, debug_loc)
                    } else {
                        build::Neg(bcx, operand.llval, debug_loc)
                    }
                };
                (bcx, OperandRef {
                    llval: llval,
                    ty: operand.ty,
                })
            }

            mir::Rvalue::Box(content_ty) => {
                let content_ty: Ty<'tcx> = bcx.monomorphize(&content_ty);
                let llty = type_of::type_of(bcx.ccx(), content_ty);
                let llsize = machine::llsize_of(bcx.ccx(), llty);
                let align = type_of::align_of(bcx.ccx(), content_ty);
                let llalign = common::C_uint(bcx.ccx(), align);
                let llty_ptr = llty.ptr_to();
                let box_ty = bcx.tcx().mk_box(content_ty);
                let Result { bcx, val: llval } = base::malloc_raw_dyn(bcx,
                                                                      llty_ptr,
                                                                      box_ty,
                                                                      llsize,
                                                                      llalign,
                                                                      DebugLoc::None);
                (bcx, OperandRef {
                    llval: llval,
                    ty: box_ty,
                })
            }

            mir::Rvalue::Repeat(..) |
            mir::Rvalue::Aggregate(..) |
            mir::Rvalue::Slice { .. } |
            mir::Rvalue::InlineAsm(..) => {
                bcx.tcx().sess.bug(&format!("cannot generate operand from rvalue {:?}", rvalue));
            }
        }
    }
}

pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool {
    match *rvalue {
        mir::Rvalue::Use(..) | // (*)
        mir::Rvalue::Ref(..) |
        mir::Rvalue::Len(..) |
        mir::Rvalue::Cast(..) | // (*)
        mir::Rvalue::BinaryOp(..) |
        mir::Rvalue::UnaryOp(..) |
        mir::Rvalue::Box(..) =>
            true,
        mir::Rvalue::Repeat(..) |
        mir::Rvalue::Aggregate(..) |
        mir::Rvalue::Slice { .. } |
        mir::Rvalue::InlineAsm(..) =>
            false,
    }

    // (*) this is only true if the type is suitable
}
<commit_msg>remove unused import<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use llvm::ValueRef;
use rustc::middle::ty::Ty;
use rustc_front::hir;
use rustc_mir::repr as mir;

use trans::asm;
use trans::base;
use trans::build;
use trans::common::{self, Block, Result};
use trans::debuginfo::DebugLoc;
use trans::declare;
use trans::expr;
use trans::machine;
use trans::type_::Type;
use trans::type_of;
use trans::tvec;

use super::MirContext;
use super::operand::OperandRef;

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
    pub fn trans_rvalue(&mut self,
                        bcx: Block<'bcx, 'tcx>,
                        lldest: ValueRef,
                        rvalue: &mir::Rvalue<'tcx>)
                        -> Block<'bcx, 'tcx>
    {
        debug!("trans_rvalue(lldest={}, rvalue={:?})",
               bcx.val_to_string(lldest),
               rvalue);

        match *rvalue {
            mir::Rvalue::Use(ref operand) => {
                self.trans_operand_into(bcx, lldest, operand);
                bcx
            }

            mir::Rvalue::Cast(..) => {
                unimplemented!()
            }

            mir::Rvalue::Repeat(..) => {
                unimplemented!()
            }

            mir::Rvalue::Aggregate(_, ref operands) => {
                for (i, operand) in operands.iter().enumerate() {
                    // Note: perhaps this should be StructGep, but
                    // note that in some cases the values here will
                    // not be structs but arrays.
                    let lldest_i = build::GEPi(bcx, lldest, &[0, i]);
                    self.trans_operand_into(bcx, lldest_i, operand);
                }
                bcx
            }

            mir::Rvalue::Slice { ref input, from_start, from_end } => {
                let ccx = bcx.ccx();
                let input = self.trans_lvalue(bcx, input);
                let (llbase, lllen) = tvec::get_base_and_len(bcx,
                                                             input.llval,
                                                             input.ty.to_ty(bcx.tcx()));
                let llbase1 = build::GEPi(bcx, llbase, &[from_start]);
                let adj = common::C_uint(ccx, from_start + from_end);
                let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None);
                let lladdrdest = expr::get_dataptr(bcx, lldest);
                build::Store(bcx, llbase1, lladdrdest);
                let llmetadest = expr::get_meta(bcx, lldest);
                build::Store(bcx, lllen1, llmetadest);
                bcx
            }

            mir::Rvalue::InlineAsm(inline_asm) => {
                asm::trans_inline_asm(bcx, inline_asm)
            }

            _ => {
                assert!(rvalue_creates_operand(rvalue));
                let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
                build::Store(bcx, temp.llval, lldest);
                bcx
            }
        }
    }

    pub fn trans_rvalue_operand(&mut self,
                                bcx: Block<'bcx, 'tcx>,
                                rvalue: &mir::Rvalue<'tcx>)
                                -> (Block<'bcx, 'tcx>, OperandRef<'tcx>)
    {
        assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);

        match *rvalue {
            mir::Rvalue::Use(ref operand) => {
                let operand = self.trans_operand(bcx, operand);
                (bcx, operand)
            }

            mir::Rvalue::Cast(..) => {
                unimplemented!()
            }

            mir::Rvalue::Ref(_, _, ref lvalue) => {
                let tr_lvalue = self.trans_lvalue(bcx, lvalue);

                // Note: lvalues are indirect, so storing the `llval` into the
                // destination effectively creates a reference.
                (bcx, OperandRef {
                    llval: tr_lvalue.llval,
                    ty: tr_lvalue.ty.to_ty(bcx.tcx()),
                })
            }

            mir::Rvalue::Len(ref lvalue) => {
                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
                let (_, lllen) = tvec::get_base_and_len(bcx,
                                                        tr_lvalue.llval,
                                                        tr_lvalue.ty.to_ty(bcx.tcx()));
                (bcx, OperandRef {
                    llval: lllen,
                    ty: bcx.tcx().types.usize,
                })
            }

            mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
                let lhs = self.trans_operand(bcx, lhs);
                let rhs = self.trans_operand(bcx, rhs);
                let is_float = lhs.ty.is_fp();
                let is_signed = lhs.ty.is_signed();
                let binop_debug_loc = DebugLoc::None;
                let llval = match op {
                    mir::BinOp::Add => if is_float {
                        build::FAdd(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else {
                        build::Add(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    },
                    mir::BinOp::Sub => if is_float {
                        build::FSub(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else {
                        build::Sub(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    },
                    mir::BinOp::Mul => if is_float {
                        build::FMul(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else {
                        build::Mul(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    },
                    mir::BinOp::Div => if is_float {
                        build::FDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else if is_signed {
                        build::SDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else {
                        build::UDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    },
                    mir::BinOp::Rem => if is_float {
                        // LLVM currently always lowers the `frem` instructions appropriate
                        // library calls typically found in libm. Notably f64 gets wired up
                        // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for
                        // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's
                        // instead just an inline function in a header that goes up to a
                        // f64, uses `fmod`, and then comes back down to a f32.
                        //
                        // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will
                        // still unconditionally lower frem instructions over 32-bit floats
                        // to a call to `fmodf`. To work around this we special case MSVC
                        // 32-bit float rem instructions and instead do the call out to
                        // `fmod` ourselves.
                        //
                        // Note that this is currently duplicated with src/libcore/ops.rs
                        // which does the same thing, and it would be nice to perhaps unify
                        // these two implementations one day! Also note that we call `fmod`
                        // for both 32 and 64-bit floats because if we emit any FRem
                        // instruction at all then LLVM is capable of optimizing it into a
                        // 32-bit FRem (which we're trying to avoid).
                        let tcx = bcx.tcx();
                        let use_fmod = tcx.sess.target.target.options.is_like_msvc &&
                            tcx.sess.target.target.arch == "x86";
                        if use_fmod {
                            let f64t = Type::f64(bcx.ccx());
                            let fty = Type::func(&[f64t, f64t], &f64t);
                            let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty,
                                                            tcx.types.f64);
                            if lhs.ty == tcx.types.f32 {
                                let lllhs = build::FPExt(bcx, lhs.llval, f64t);
                                let llrhs = build::FPExt(bcx, rhs.llval, f64t);
                                let llres = build::Call(bcx, llfn, &[lllhs, llrhs],
                                                        None, binop_debug_loc);
                                build::FPTrunc(bcx, llres, Type::f32(bcx.ccx()))
                            } else {
                                build::Call(bcx, llfn, &[lhs.llval, rhs.llval],
                                            None, binop_debug_loc)
                            }
                        } else {
                            build::FRem(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                        }
                    } else if is_signed {
                        build::SRem(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    } else {
                        build::URem(bcx, lhs.llval, rhs.llval, binop_debug_loc)
                    },
                    mir::BinOp::BitOr => build::Or(bcx, lhs.llval, rhs.llval, binop_debug_loc),
                    mir::BinOp::BitAnd => build::And(bcx, lhs.llval, rhs.llval, binop_debug_loc),
                    mir::BinOp::BitXor => build::Xor(bcx, lhs.llval, rhs.llval, binop_debug_loc),
                    mir::BinOp::Shl => common::build_unchecked_lshift(bcx,
                                                                      lhs.llval,
                                                                      rhs.llval,
                                                                      binop_debug_loc),
                    mir::BinOp::Shr => common::build_unchecked_rshift(bcx,
                                                                      lhs.ty,
                                                                      lhs.llval,
                                                                      rhs.llval,
                                                                      binop_debug_loc),
                    mir::BinOp::Eq => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiEq, binop_debug_loc),
                    mir::BinOp::Lt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiLt, binop_debug_loc),
                    mir::BinOp::Le => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiLe, binop_debug_loc),
                    mir::BinOp::Ne => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiNe, binop_debug_loc),
                    mir::BinOp::Ge => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiGe, binop_debug_loc),
                    mir::BinOp::Gt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty,
                                                                 hir::BiGt, binop_debug_loc),
                };
                (bcx, OperandRef {
                    llval: llval,
                    ty: lhs.ty,
                })
            }

            mir::Rvalue::UnaryOp(op, ref operand) => {
                let operand = self.trans_operand(bcx, operand);
                let is_float = operand.ty.is_fp();
                let debug_loc = DebugLoc::None;
                let llval = match op {
                    mir::UnOp::Not => build::Not(bcx, operand.llval, debug_loc),
                    mir::UnOp::Neg => if is_float {
                        build::FNeg(bcx, operand.llval, debug_loc)
                    } else {
                        build::Neg(bcx, operand.llval, debug_loc)
                    }
                };
                (bcx, OperandRef {
                    llval: llval,
                    ty: operand.ty,
                })
            }

            mir::Rvalue::Box(content_ty) => {
                let content_ty: Ty<'tcx> = bcx.monomorphize(&content_ty);
                let llty = type_of::type_of(bcx.ccx(), content_ty);
                let llsize = machine::llsize_of(bcx.ccx(), llty);
                let align = type_of::align_of(bcx.ccx(), content_ty);
                let llalign = common::C_uint(bcx.ccx(), align);
                let llty_ptr = llty.ptr_to();
                let box_ty = bcx.tcx().mk_box(content_ty);
                let Result { bcx, val: llval } = base::malloc_raw_dyn(bcx,
                                                                      llty_ptr,
                                                                      box_ty,
                                                                      llsize,
                                                                      llalign,
                                                                      DebugLoc::None);
                (bcx, OperandRef {
                    llval: llval,
                    ty: box_ty,
                })
            }

            mir::Rvalue::Repeat(..) |
            mir::Rvalue::Aggregate(..) |
            mir::Rvalue::Slice { .. } |
            mir::Rvalue::InlineAsm(..) => {
                bcx.tcx().sess.bug(&format!("cannot generate operand from rvalue {:?}", rvalue));
            }
        }
    }
}

pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool {
    match *rvalue {
        mir::Rvalue::Use(..) | // (*)
        mir::Rvalue::Ref(..) |
        mir::Rvalue::Len(..) |
        mir::Rvalue::Cast(..) | // (*)
        mir::Rvalue::BinaryOp(..) |
        mir::Rvalue::UnaryOp(..) |
        mir::Rvalue::Box(..) =>
            true,
        mir::Rvalue::Repeat(..) |
        mir::Rvalue::Aggregate(..) |
        mir::Rvalue::Slice { .. } |
        mir::Rvalue::InlineAsm(..) =>
            false,
    }

    // (*) this is only true if the type is suitable
}
<|endoftext|>


<commit_before>// Functions for building ASTs, without having to fuss with spans.
//
// To start with, it will be use dummy spans, but it might someday do
// something smarter.

use ast::{ident, node_id};
use ast_util::respan;
use codemap::span;
use ext::base::mk_ctxt;

// Transitional reexports so qquote can find the paths it is looking for
mod syntax {
    #[legacy_exports];
    pub use ext;
    pub use parse;
}

fn path(ids: ~[ident], span: span) -> @ast::path {
    @{span: span,
      global: false,
      idents: ids,
      rp: None,
      types: ~[]}
}

fn empty_span() -> span {
    {lo: 0, hi: 0, expn_info: None}
}

trait append_types {
    fn add_ty(ty: @ast::Ty) -> @ast::path;
    fn add_tys(+tys: ~[@ast::Ty]) -> @ast::path;
}

impl @ast::path: append_types {
    fn add_ty(ty: @ast::Ty) -> @ast::path {
        @{types: vec::append_one(self.types, ty),
          .. *self}
    }

    fn add_tys(+tys: ~[@ast::Ty]) -> @ast::path {
        @{types: vec::append(self.types, tys),
          .. *self}
    }
}

trait ext_ctxt_ast_builder {
    fn ty_param(id: ast::ident, +bounds: ~[ast::ty_param_bound])
        -> ast::ty_param;
    fn arg(name: ident, ty: @ast::Ty) -> ast::arg;
    fn expr_block(e: @ast::expr) -> ast::blk;
    fn fn_decl(+inputs: ~[ast::arg], output: @ast::Ty) -> ast::fn_decl;
    fn item(name: ident, span: span, +node: ast::item_) -> @ast::item;
    fn item_fn_poly(name: ident,
                    +inputs: ~[ast::arg],
                    output: @ast::Ty,
                    +ty_params: ~[ast::ty_param],
                    +body: ast::blk) -> @ast::item;
    fn item_fn(name: ident,
               +inputs: ~[ast::arg],
               output: @ast::Ty,
               +body: ast::blk) -> @ast::item;
    fn item_enum_poly(name: ident,
                      span: span,
                      +enum_definition: ast::enum_def,
                      +ty_params: ~[ast::ty_param]) -> @ast::item;
    fn item_enum(name: ident, span: span,
                 +enum_definition: ast::enum_def) -> @ast::item;
    fn variant(name: ident, span: span, +tys: ~[@ast::Ty]) -> ast::variant;
    fn item_mod(name: ident, span: span, +items: ~[@ast::item]) -> @ast::item;
    fn ty_path_ast_builder(path: @ast::path) -> @ast::Ty;
    fn item_ty_poly(name: ident,
                    span: span,
                    ty: @ast::Ty,
                    +params: ~[ast::ty_param]) -> @ast::item;
    fn item_ty(name: ident, span: span, ty: @ast::Ty) -> @ast::item;
    fn ty_vars(+ty_params: ~[ast::ty_param]) -> ~[@ast::Ty];
    fn ty_field_imm(name: ident, ty: @ast::Ty) -> ast::ty_field;
    fn ty_rec(+v: ~[ast::ty_field]) -> @ast::Ty;
    fn field_imm(name: ident, e: @ast::expr) -> ast::field;
    fn rec(+v: ~[ast::field]) -> @ast::expr;
    fn block(+stmts: ~[@ast::stmt], e: @ast::expr) -> ast::blk;
    fn stmt_let(ident: ident, e: @ast::expr) -> @ast::stmt;
    fn stmt_expr(e: @ast::expr) -> @ast::stmt;
    fn block_expr(b: ast::blk) -> @ast::expr;
    fn empty_span() -> span;
    fn ty_option(ty: @ast::Ty) -> @ast::Ty;
}

impl ext_ctxt: ext_ctxt_ast_builder {
    fn ty_option(ty: @ast::Ty) -> @ast::Ty {
        self.ty_path_ast_builder(path(~[self.ident_of(~"Option")],
                                      self.empty_span())
                                 .add_ty(ty))
    }

    fn empty_span() -> span {
        {lo: 0, hi: 0, expn_info: self.backtrace()}
    }

    fn block_expr(b: ast::blk) -> @ast::expr {
        @{id: self.next_id(),
          callee_id: self.next_id(),
          node: ast::expr_block(b),
          span: self.empty_span()}
    }

    fn stmt_expr(e: @ast::expr) -> @ast::stmt {
        @{node: ast::stmt_expr(e, self.next_id()),
          span: self.empty_span()}
    }

    fn stmt_let(ident: ident, e: @ast::expr) -> @ast::stmt {
        // If the quasiquoter could interpolate idents, this is all
        // we'd need.
        //
        //let ext_cx = self;
        //#ast[stmt] { let $(ident) = $(e) }

        @{node: ast::stmt_decl(@{node: ast::decl_local(~[
            @{node: {is_mutbl: false,
                     ty: self.ty_infer(),
                     pat: @{id: self.next_id(),
                            node: ast::pat_ident(ast::bind_by_implicit_ref,
                                                 path(~[ident],
                                                      self.empty_span()),
                                                 None),
                            span: self.empty_span()},
                     init: Some({op: ast::init_move,
                                 expr: e}),
                     id: self.next_id()},
              span: self.empty_span()}]),
                               span: self.empty_span()}, self.next_id()),
         span: self.empty_span()}
    }

    fn field_imm(name: ident, e: @ast::expr) -> ast::field {
        {node: {mutbl: ast::m_imm, ident: name, expr: e},
         span: self.empty_span()}
    }

    fn rec(+fields: ~[ast::field]) -> @ast::expr {
        @{id: self.next_id(),
          callee_id: self.next_id(),
          node: ast::expr_rec(fields, None),
          span: self.empty_span()}
    }

    fn ty_field_imm(name: ident, ty: @ast::Ty) -> ast::ty_field {
        {node: {ident: name, mt: { ty: ty, mutbl: ast::m_imm } },
          span: self.empty_span()}
    }

    fn ty_rec(+fields: ~[ast::ty_field]) -> @ast::Ty {
        @{id: self.next_id(),
          node: ast::ty_rec(fields),
          span: self.empty_span()}
    }

    fn ty_infer() -> @ast::Ty {
        @{id: self.next_id(),
          node: ast::ty_infer,
          span: self.empty_span()}
    }

    fn ty_param(id: ast::ident, +bounds: ~[ast::ty_param_bound])
        -> ast::ty_param
    {
        {ident: id, id: self.next_id(), bounds: @bounds}
    }

    fn arg(name: ident, ty: @ast::Ty) -> ast::arg {
        {mode: ast::infer(self.next_id()),
         ty: ty,
         ident: name,
         id: self.next_id()}
    }

    fn block(+stmts: ~[@ast::stmt], e: @ast::expr) -> ast::blk {
        let blk = {view_items: ~[],
                   stmts: stmts,
                   expr: Some(e),
                   id: self.next_id(),
                   rules: ast::default_blk};

        {node: blk,
         span: self.empty_span()}
    }

    fn expr_block(e: @ast::expr) -> ast::blk {
        self.block(~[], e)
    }

    fn fn_decl(+inputs: ~[ast::arg],
               output: @ast::Ty) -> ast::fn_decl {
        {inputs: inputs,
         output: output,
         cf: ast::return_val}
    }

    fn item(name: ident,
            span: span,
            +node: ast::item_) -> @ast::item {

        // XXX: Would be nice if our generated code didn't violate
        // Rust coding conventions
        let non_camel_case_attribute = respan(self.empty_span(), {
            style: ast::attr_outer,
            value: respan(self.empty_span(),
                          ast::meta_list(~"allow", ~[
                              @respan(self.empty_span(),
                                      ast::meta_word(~"non_camel_case_types"))
                          ])),
            is_sugared_doc: false
        });

        @{ident: name,
         attrs: ~[non_camel_case_attribute],
         id: self.next_id(),
         node: node,
         vis: ast::public,
         span: span}
    }

    fn item_fn_poly(name: ident,
                    +inputs: ~[ast::arg],
                    output: @ast::Ty,
                    +ty_params: ~[ast::ty_param],
                    +body: ast::blk) -> @ast::item {
        self.item(name,
                  self.empty_span(),
                  ast::item_fn(self.fn_decl(inputs, output),
                               ast::impure_fn,
                               ty_params,
                               body))
    }

    fn item_fn(name: ident,
               +inputs: ~[ast::arg],
               output: @ast::Ty,
               +body: ast::blk) -> @ast::item {
        self.item_fn_poly(name, inputs, output, ~[], body)
    }

    fn item_enum_poly(name: ident,
                      span: span,
                      +enum_definition: ast::enum_def,
                      +ty_params: ~[ast::ty_param]) -> @ast::item {
        self.item(name, span, ast::item_enum(enum_definition, ty_params))
    }

    fn item_enum(name: ident, span: span,
                 +enum_definition: ast::enum_def) -> @ast::item {
        self.item_enum_poly(name, span, enum_definition, ~[])
    }

    fn variant(name: ident,
               span: span,
               +tys: ~[@ast::Ty]) -> ast::variant {
        let args = tys.map(|ty| {ty: *ty, id: self.next_id()});

        {node: {name: name,
                attrs: ~[],
                kind: ast::tuple_variant_kind(args),
                id: self.next_id(),
                disr_expr: None,
                vis: ast::public},
         span: span}
    }

    fn item_mod(name: ident,
                span: span,
                +items: ~[@ast::item]) -> @ast::item {
        self.item(name,
                  span,
                  ast::item_mod({
                      view_items: ~[],
                      items: items}))
    }

    fn ty_path_ast_builder(path: @ast::path) -> @ast::Ty {
        @{id: self.next_id(),
          node: ast::ty_path(path, self.next_id()),
          span: path.span}
    }

    fn ty_nil_ast_builder() -> @ast::Ty {
        @{id: self.next_id(),
          node: ast::ty_nil,
          span: self.empty_span()}
    }

    fn item_ty_poly(name: ident,
                    span: span,
                    ty: @ast::Ty,
                    +params: ~[ast::ty_param]) -> @ast::item {
        self.item(name, span, ast::item_ty(ty, params))
    }

    fn item_ty(name: ident, span: span, ty: @ast::Ty) -> @ast::item {
        self.item_ty_poly(name, span, ty, ~[])
    }

    fn ty_vars(+ty_params: ~[ast::ty_param]) -> ~[@ast::Ty] {
        ty_params.map(|p| self.ty_path_ast_builder(
            path(~[p.ident], self.empty_span())))
    }
}
<commit_msg>Don't generate move-by-init in AST builder<commit_after>// Functions for building ASTs, without having to fuss with spans.
//
// To start with, it will be use dummy spans, but it might someday do
// something smarter.

use ast::{ident, node_id};
use ast_util::respan;
use codemap::span;
use ext::base::mk_ctxt;

// Transitional reexports so qquote can find the paths it is looking for
mod syntax {
    #[legacy_exports];
    pub use ext;
    pub use parse;
}

fn path(ids: ~[ident], span: span) -> @ast::path {
    @{span: span,
      global: false,
      idents: ids,
      rp: None,
      types: ~[]}
}

fn empty_span() -> span {
    {lo: 0, hi: 0, expn_info: None}
}

trait append_types {
    fn add_ty(ty: @ast::Ty) -> @ast::path;
    fn add_tys(+tys: ~[@ast::Ty]) -> @ast::path;
}

impl @ast::path: append_types {
    fn add_ty(ty: @ast::Ty) -> @ast::path {
        @{types: vec::append_one(self.types, ty),
          .. *self}
    }

    fn add_tys(+tys: ~[@ast::Ty]) -> @ast::path {
        @{types: vec::append(self.types, tys),
          .. *self}
    }
}

trait ext_ctxt_ast_builder {
    fn ty_param(id: ast::ident, +bounds: ~[ast::ty_param_bound])
        -> ast::ty_param;
    fn arg(name: ident, ty: @ast::Ty) -> ast::arg;
    fn expr_block(e: @ast::expr) -> ast::blk;
    fn fn_decl(+inputs: ~[ast::arg], output: @ast::Ty) -> ast::fn_decl;
    fn item(name: ident, span: span, +node: ast::item_) -> @ast::item;
    fn item_fn_poly(name: ident,
                    +inputs: ~[ast::arg],
                    output: @ast::Ty,
                    +ty_params: ~[ast::ty_param],
                    +body: ast::blk) -> @ast::item;
    fn item_fn(name: ident,
               +inputs: ~[ast::arg],
               output: @ast::Ty,
               +body: ast::blk) -> @ast::item;
    fn item_enum_poly(name: ident,
                      span: span,
                      +enum_definition: ast::enum_def,
                      +ty_params: ~[ast::ty_param]) -> @ast::item;
    fn item_enum(name: ident, span: span,
                 +enum_definition: ast::enum_def) -> @ast::item;
    fn variant(name: ident, span: span, +tys: ~[@ast::Ty]) -> ast::variant;
    fn item_mod(name: ident, span: span, +items: ~[@ast::item]) -> @ast::item;
    fn ty_path_ast_builder(path: @ast::path) -> @ast::Ty;
    fn item_ty_poly(name: ident,
                    span: span,
                    ty: @ast::Ty,
                    +params: ~[ast::ty_param]) -> @ast::item;
    fn item_ty(name: ident, span: span, ty: @ast::Ty) -> @ast::item;
    fn ty_vars(+ty_params: ~[ast::ty_param]) -> ~[@ast::Ty];
    fn ty_field_imm(name: ident, ty: @ast::Ty) -> ast::ty_field;
    fn ty_rec(+v: ~[ast::ty_field]) -> @ast::Ty;
    fn field_imm(name: ident, e: @ast::expr) -> ast::field;
    fn rec(+v: ~[ast::field]) -> @ast::expr;
    fn block(+stmts: ~[@ast::stmt], e: @ast::expr) -> ast::blk;
    fn stmt_let(ident: ident, e: @ast::expr) -> @ast::stmt;
    fn stmt_expr(e: @ast::expr) -> @ast::stmt;
    fn block_expr(b: ast::blk) -> @ast::expr;
    fn empty_span() -> span;
    fn ty_option(ty: @ast::Ty) -> @ast::Ty;
}

impl ext_ctxt: ext_ctxt_ast_builder {
    fn ty_option(ty: @ast::Ty) -> @ast::Ty {
        self.ty_path_ast_builder(path(~[self.ident_of(~"Option")],
                                      self.empty_span())
                                 .add_ty(ty))
    }

    fn empty_span() -> span {
        {lo: 0, hi: 0, expn_info: self.backtrace()}
    }

    fn block_expr(b: ast::blk) -> @ast::expr {
        @{id: self.next_id(),
          callee_id: self.next_id(),
          node: ast::expr_block(b),
          span: self.empty_span()}
    }

    fn move_expr(e: @ast::expr) -> @ast::expr {
        @{id: self.next_id(),
          callee_id: self.next_id(),
          node: ast::expr_unary_move(e),
          span: e.span}
    }

    fn stmt_expr(e: @ast::expr) -> @ast::stmt {
        @{node: ast::stmt_expr(e, self.next_id()),
          span: self.empty_span()}
    }

    fn stmt_let(ident: ident, e: @ast::expr) -> @ast::stmt {
        // If the quasiquoter could interpolate idents, this is all
        // we'd need.
        //
        //let ext_cx = self;
        //#ast[stmt] { let $(ident) = $(e) }

        @{node: ast::stmt_decl(@{node: ast::decl_local(~[
            @{node: {is_mutbl: false,
                     ty: self.ty_infer(),
                     pat: @{id: self.next_id(),
                            node: ast::pat_ident(ast::bind_by_implicit_ref,
                                                 path(~[ident],
                                                      self.empty_span()),
                                                 None),
                            span: self.empty_span()},
                     init: Some(self.move_expr(e)),
                     id: self.next_id()},
              span: self.empty_span()}]),
                               span: self.empty_span()}, self.next_id()),
         span: self.empty_span()}
    }

    fn field_imm(name: ident, e: @ast::expr) -> ast::field {
        {node: {mutbl: ast::m_imm, ident: name, expr: e},
         span: self.empty_span()}
    }

    fn rec(+fields: ~[ast::field]) -> @ast::expr {
        @{id: self.next_id(),
          callee_id: self.next_id(),
          node: ast::expr_rec(fields, None),
          span: self.empty_span()}
    }

    fn ty_field_imm(name: ident, ty: @ast::Ty) -> ast::ty_field {
        {node: {ident: name, mt: { ty: ty, mutbl: ast::m_imm } },
          span: self.empty_span()}
    }

    fn ty_rec(+fields: ~[ast::ty_field]) -> @ast::Ty {
        @{id: self.next_id(),
          node: ast::ty_rec(fields),
          span: self.empty_span()}
    }

    fn ty_infer() -> @ast::Ty {
        @{id: self.next_id(),
          node: ast::ty_infer,
          span: self.empty_span()}
    }

    fn ty_param(id: ast::ident, +bounds: ~[ast::ty_param_bound])
        -> ast::ty_param
    {
        {ident: id, id: self.next_id(), bounds: @bounds}
    }

    fn arg(name: ident, ty: @ast::Ty) -> ast::arg {
        {mode: ast::infer(self.next_id()),
         ty: ty,
         ident: name,
         id: self.next_id()}
    }

    fn block(+stmts: ~[@ast::stmt], e: @ast::expr) -> ast::blk {
        let blk = {view_items: ~[],
                   stmts: stmts,
                   expr: Some(e),
                   id: self.next_id(),
                   rules: ast::default_blk};

        {node: blk,
         span: self.empty_span()}
    }

    fn expr_block(e: @ast::expr) -> ast::blk {
        self.block(~[], e)
    }

    fn fn_decl(+inputs: ~[ast::arg],
               output: @ast::Ty) -> ast::fn_decl {
        {inputs: inputs,
         output: output,
         cf: ast::return_val}
    }

    fn item(name: ident,
            span: span,
            +node: ast::item_) -> @ast::item {

        // XXX: Would be nice if our generated code didn't violate
        // Rust coding conventions
        let non_camel_case_attribute = respan(self.empty_span(), {
            style: ast::attr_outer,
            value: respan(self.empty_span(),
                          ast::meta_list(~"allow", ~[
                              @respan(self.empty_span(),
                                      ast::meta_word(~"non_camel_case_types"))
                          ])),
            is_sugared_doc: false
        });

        @{ident: name,
         attrs: ~[non_camel_case_attribute],
         id: self.next_id(),
         node: node,
         vis: ast::public,
         span: span}
    }

    fn item_fn_poly(name: ident,
                    +inputs: ~[ast::arg],
                    output: @ast::Ty,
                    +ty_params: ~[ast::ty_param],
                    +body: ast::blk) -> @ast::item {
        self.item(name,
                  self.empty_span(),
                  ast::item_fn(self.fn_decl(inputs, output),
                               ast::impure_fn,
                               ty_params,
                               body))
    }

    fn item_fn(name: ident,
               +inputs: ~[ast::arg],
               output: @ast::Ty,
               +body: ast::blk) -> @ast::item {
        self.item_fn_poly(name, inputs, output, ~[], body)
    }

    fn item_enum_poly(name: ident,
                      span: span,
                      +enum_definition: ast::enum_def,
                      +ty_params: ~[ast::ty_param]) -> @ast::item {
        self.item(name, span, ast::item_enum(enum_definition, ty_params))
    }

    fn item_enum(name: ident, span: span,
                 +enum_definition: ast::enum_def) -> @ast::item {
        self.item_enum_poly(name, span, enum_definition, ~[])
    }

    fn variant(name: ident,
               span: span,
               +tys: ~[@ast::Ty]) -> ast::variant {
        let args = tys.map(|ty| {ty: *ty, id: self.next_id()});

        {node: {name: name,
                attrs: ~[],
                kind: ast::tuple_variant_kind(args),
                id: self.next_id(),
                disr_expr: None,
                vis: ast::public},
         span: span}
    }

    fn item_mod(name: ident,
                span: span,
                +items: ~[@ast::item]) -> @ast::item {
        self.item(name,
                  span,
                  ast::item_mod({
                      view_items: ~[],
                      items: items}))
    }

    fn ty_path_ast_builder(path: @ast::path) -> @ast::Ty {
        @{id: self.next_id(),
          node: ast::ty_path(path, self.next_id()),
          span: path.span}
    }

    fn ty_nil_ast_builder() -> @ast::Ty {
        @{id: self.next_id(),
          node: ast::ty_nil,
          span: self.empty_span()}
    }

    fn item_ty_poly(name: ident,
                    span: span,
                    ty: @ast::Ty,
                    +params: ~[ast::ty_param]) -> @ast::item {
        self.item(name, span, ast::item_ty(ty, params))
    }

    fn item_ty(name: ident, span: span, ty: @ast::Ty) -> @ast::item {
        self.item_ty_poly(name, span, ty, ~[])
    }

    fn ty_vars(+ty_params: ~[ast::ty_param]) -> ~[@ast::Ty] {
        ty_params.map(|p| self.ty_path_ast_builder(
            path(~[p.ident], self.empty_span())))
    }
}
<|endoftext|>


<commit_before>use config::action::ActionType;
use config::Context;
use serde::de::{Deserialize, Deserializer, MapVisitor, Error, Visitor};

use handlebars::Template;
use uuid::Uuid;

const FIELDS: &'static [&'static str] = &["name", "uuid", "conditions", "actions"];

impl Deserialize for Context {
    fn deserialize<D>(deserializer: &mut D) -> Result<Context, D::Error>
        where D: Deserializer
    {
        deserializer.visit_struct("Context", FIELDS, ContextVisitor)
    }
}

enum Field {
    Name,
    Uuid,
    Conditions,
    ContextId,
    Actions,
}

impl Deserialize for Field {
    fn deserialize<D>(deserializer: &mut D) -> Result<Field, D::Error>
        where D: Deserializer
    {
        struct FieldVisitor;

        impl Visitor for FieldVisitor {
            type Value = Field;

            fn visit_str<E>(&mut self, value: &str) -> Result<Field, E>
                where E: Error
            {
                match value {
                    "name" => Ok(Field::Name),
                    "uuid" => Ok(Field::Uuid),
                    "conditions" => Ok(Field::Conditions),
                    "context_id" => Ok(Field::ContextId),
                    "actions" => Ok(Field::Actions),
                    _ => Err(Error::syntax(&format!("Unexpected field: {}", value))),
                }
            }
        }

        deserializer.visit(FieldVisitor)
    }
}

struct ContextVisitor;

impl ContextVisitor {
    fn parse_uuid<V>(uuid: Option<String>) -> Result<Uuid, V::Error>
        where V: MapVisitor
    {
        match uuid {
            Some(value) => {
                match Uuid::parse_str(&value) {
                    Ok(uuid) => Ok(uuid),
                    Err(err) => {
                        return Err(Error::syntax(&format!("Failed to parse field 'uuid': \
                                                           uuid={} error={}",
                                                          value,
                                                          err)));
                    }
                }
            }
            None => {
                return Err(Error::missing_field("uuid"));
            }
        }
    }

    fn deser_context_id<V>(context_id: Option<String>,
                           uuid: &Uuid)
                           -> Result<Option<Template>, V::Error>
        where V: MapVisitor
    {
        if let Some(context_id) = context_id {
            ContextVisitor::parse_context_id::<V>(context_id, uuid)
        } else {
            Ok(None)
        }
    }

    fn parse_context_id<V>(context_id: String, uuid: &Uuid) -> Result<Option<Template>, V::Error>
        where V: MapVisitor
    {
        match Template::compile(context_id) {
            Ok(context_id) => Ok(Some(context_id)),
            Err(err) => {
                let errmsg = format!("Invalid handlebars template in 'context_id' field: uuid={} \
                                      error={}",
                                     uuid,
                                     err);
                return Err(Error::syntax(&errmsg));
            }
        }
    }

    fn parse_actions<V>(actions: Option<Vec<ActionType>>) -> Result<Vec<ActionType>, V::Error>
        where V: MapVisitor
    {
        match actions {
            Some(actions) => Ok(actions),
            None => Ok(Vec::new()),
        }
    }
}

impl Visitor for ContextVisitor {
    type Value = Context;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<Context, V::Error>
        where V: MapVisitor
    {
        let mut name = None;
        let mut uuid: Option<String> = None;
        let mut conditions = None;
        let mut context_id: Option<String> = None;
        let mut actions = None;

        while let Some(field) = try!(visitor.visit_key()) {
            match field {
                Field::Name => name = Some(try!(visitor.visit_value())),
                Field::Uuid => uuid = Some(try!(visitor.visit_value())),
                Field::Conditions => conditions = Some(try!(visitor.visit_value())),
                Field::ContextId => context_id = Some(try!(visitor.visit_value())),
                Field::Actions => actions = Some(try!(visitor.visit_value())),
            }
        }

        let uuid = try!(ContextVisitor::parse_uuid::<V>(uuid));
        let context_id = try!(ContextVisitor::deser_context_id::<V>(context_id, &uuid));
        let actions = try!(ContextVisitor::parse_actions::<V>(actions));

        try!(visitor.end());

        Ok(Context {
            name: name,
            uuid: uuid,
            conditions: conditions.unwrap(),
            context_id: context_id,
            actions: actions,
        })
    }
}

#[cfg(test)]
mod test {
    use config::action::{ActionType, ExecCondition};
    use config::action::message::MessageActionBuilder;
    use conditions::ConditionsBuilder;
    use config::Context;
    use handlebars::Template;
    use serde_json::from_str;
    use uuid::Uuid;
    use std::time::Duration;

    #[test]
    fn test_given_config_context_when_it_is_deserialized_then_we_get_the_right_results() {
        let text = r#"
        {
            "name": "TEST_NAME",
            "uuid": "86ca9f93-84fb-4813-b037-6526f7a585a3",
            "conditions": {
                "timeout": 100,
                "first_opens": true,
                "patterns": [
                    "PATTERN_NAME1",
                    "PATTERN_NAME2",
                    "f13dafee-cd14-4dda-995c-6ed476a21de3"
                ]
            },
            "actions": [
                {
                    "message": {
                        "uuid": "uuid1",
                        "when": {
                            "on_closed": true,
                            "on_opened": false
                        },
                        "message": "message"
                    }
                }
            ]
        }
        "#;

        let result = from_str::<Context>(text);
        println!("{:?}", &result);
        let expected_name = "TEST_NAME".to_string();
        let expected_uuid = Uuid::parse_str("86ca9f93-84fb-4813-b037-6526f7a585a3").ok().unwrap();
        let expected_conditions = ConditionsBuilder::new(Duration::from_millis(100))
                                      .first_opens(true)
                                      .patterns(vec!["PATTERN_NAME1".to_string(),
                                                     "PATTERN_NAME2".to_string(),
                                                     "f13dafee-cd14-4dda-995c-6ed476a21de3"
                                                         .to_string()])
                                      .build();
        let message = Template::compile("message".to_string())
                          .ok()
                          .expect("Failed to compile a handlebars template");
        let expected_exec_cond = ExecCondition {
            on_opened: false,
            on_closed: true,
        };
        let expected_actions = vec![ActionType::Message(MessageActionBuilder::new("uuid1",
                                                                                message)
                                                            .when(expected_exec_cond)
                                                            .build())];
        let context = result.ok().expect("Failed to deserialize a valid Context");
        assert_eq!(&Some(expected_name), &context.name);
        assert_eq!(&expected_uuid, &context.uuid);
        assert_eq!(&expected_conditions, &context.conditions);
        assert_eq!(&expected_actions, &context.actions);
    }

    #[test]
    fn test_given_config_context_when_it_is_deserialized_and_only_the_required_fields_are_present_then_we_can_deserialize_it_successfully
        () {
        let text = r#"
        {
            "uuid": "86ca9f93-84fb-4813-b037-6526f7a585a3",
            "conditions": {
                "timeout": 100
            }
        }
        "#;

        let result = from_str::<Context>(text);
        println!("{:?}", &result);
        let expected_uuid = Uuid::parse_str("86ca9f93-84fb-4813-b037-6526f7a585a3").ok().unwrap();
        let expected_conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let context = result.ok().expect("Failed to deserialize a valid Context");
        assert_eq!(&expected_uuid, &context.uuid);
        assert_eq!(&expected_conditions, &context.conditions);
    }

    #[test]
    fn test_given_config_context_when_it_is_deserialized_and_the_uuid_is_invalid_then_we_report_an_error
        () {
        let text = r#"
        {
            "uuid": "this is an invalid uuid",
            "conditions": {
                "timeout": 100
            }
        }
        "#;

        let result = from_str::<Context>(text);
        println!("{:?}", &result);
        let _ = result.err()
                      .expect("Successfully deserialized an invalid Context (UUID is invalid)");
    }

    #[test]
    fn test_given_config_context_when_it_contains_context_id_then_can_be_deserialized() {
        let text = r#"
        {
            "uuid": "86ca9f93-84fb-4813-b037-6526f7a585a3",
            "context_id": "{{HOST}}{{PROGRAM}}",
            "conditions": {
                "timeout": 100
            }
        }
        "#;
        let expected_context_id = "{{HOST}}{{PROGRAM}}".to_string();
        let result = from_str::<Context>(text);
        println!("{:?}", &result);
        let context = result.ok().expect("Failed to deserialize a valid Context");
        assert_eq!(&expected_context_id,
                   &context.context_id.as_ref().unwrap().to_string());
    }
}
<commit_msg>config/deser: simplify ContextVisitor<commit_after>use config::Context;
use serde::de::{Deserialize, Deserializer, MapVisitor, Error, Visitor};

use handlebars::Template;
use uuid::Uuid;

const FIELDS: &'static [&'static str] = &["name", "uuid", "conditions", "actions"];

impl Deserialize for Context {
    fn deserialize<D>(deserializer: &mut D) -> Result<Context, D::Error>
        where D: Deserializer
    {
        deserializer.visit_struct("Context", FIELDS, ContextVisitor)
    }
}

enum Field {
    Name,
    Uuid,
    Conditions,
    ContextId,
    Actions,
}

impl Deserialize for Field {
    fn deserialize<D>(deserializer: &mut D) -> Result<Field, D::Error>
        where D: Deserializer
    {
        struct FieldVisitor;

        impl Visitor for FieldVisitor {
            type Value = Field;

            fn visit_str<E>(&mut self, value: &str) -> Result<Field, E>
                where E: Error
            {
                match value {
                    "name" => Ok(Field::Name),
                    "uuid" => Ok(Field::Uuid),
                    "conditions" => Ok(Field::Conditions),
                    "context_id" => Ok(Field::ContextId),
                    "actions" => Ok(Field::Actions),
                    _ => Err(Error::syntax(&format!("Unexpected field: {}", value))),
                }
            }
        }

        deserializer.visit(FieldVisitor)
    }
}

struct ContextVisitor;

impl ContextVisitor {
    fn parse_uuid<V>(uuid: Option<String>) -> Result<Uuid, V::Error>
        where V: MapVisitor
    {
        match uuid {
            Some(value) => {
                match Uuid::parse_str(&value) {
                    Ok(uuid) => Ok(uuid),
                    Err(err) => {
                        return Err(Error::syntax(&format!("Failed to parse field 'uuid': \
                                                           uuid={} error={}",
                                                          value,
                                                          err)));
                    }
                }
            }
            None => {
                return Err(Error::missing_field("uuid"));
            }
        }
    }

    fn deser_context_id<V>(context_id: Option<String>,
                           uuid: &Uuid)
                           -> Result<Option<Template>, V::Error>
        where V: MapVisitor
    {
        if let Some(context_id) = context_id {
            ContextVisitor::parse_context_id::<V>(context_id, uuid)
        } else {
            Ok(None)
        }
    }

    fn parse_context_id<V>(context_id: String, uuid: &Uuid) -> Result<Option<Template>, V::Error>
        where V: MapVisitor
    {
        match Template::compile(context_id) {
            Ok(context_id) => Ok(Some(context_id)),
            Err(err) => {
                let errmsg = format!("Invalid handlebars template in 'context_id' field: uuid={} \
                                      error={}",
                                     uuid,
                                     err);
                return Err(Error::syntax(&errmsg));
            }
        }
    }
}

impl Visitor for ContextVisitor {
    type Value = Context;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<Context, V::Error>
        where V: MapVisitor
    {
        let mut name = None;
        let mut uuid: Option<String> = None;
        let mut conditions = None;
        let mut context_id: Option<String> = None;
        let mut actions = None;

        while let Some(field) = try!(visitor.visit_key()) {
            match field {
                Field::Name => name = Some(try!(visitor.visit_value())),
                Field::Uuid => uuid = Some(try!(visitor.visit_value())),
                Field::Conditions => conditions = Some(try!(visitor.visit_value())),
                Field::ContextId => context_id = Some(try!(visitor.visit_value())),
                Field::Actions => actions = Some(try!(visitor.visit_value())),
            }
        }

        let uuid = try!(ContextVisitor::parse_uuid::<V>(uuid));
        let context_id = try!(ContextVisitor::deser_context_id::<V>(context_id, &uuid));
        let actions = actions.unwrap_or(Vec::new());

        try!(visitor.end());

        Ok(Context {
            name: name,
            uuid: uuid,
            conditions: conditions.unwrap(),
            context_id: context_id,
            actions: actions,
        })
    }
}

#[cfg(test)]
mod test {
    use config::action::{ActionType, ExecCondition};
    use config::action::message::MessageActionBuilder;
    use conditions::ConditionsBuilder;
    use config::Context;
    use handlebars::Template;
    use serde_json::from_str;
    use uuid::Uuid;
    use std::time::Duration;

    #[test]
    fn test_given_config_context_when_it_is_deserialized_then_we_get_the_right_results() {
        let text = r#"
        {
            "name": "TEST_NAME",
            "uuid": "86ca9f93-84fb-4813-b037-6526f7a585a3",
            "conditions": {
                "timeout": 100,
                "first_opens": true,
                "patterns": [
                    "PATTERN_NAME1",
                    "PATTERN_NAME2",
                    "f13dafee-cd14-4dda-995c-6ed476a21de3"
                ]
            },
            "actions": [
                {
                    "message": {
                        "uuid": "uuid1",
                        "when": {
                            "on_closed": true,
                            "on_opened": false
                        },
                        "message": "message"
                    }
                }
            ]
        }
        "#;

        let result = from_str::<Context>(text);
        println!("{:?}", &result);
        let expected_name = "TEST_NAME".to_string();
        let expected_uuid = Uuid::parse_str("86ca9f93-84fb-4813-b037-6526f7a585a3").ok().unwrap();
        let expected_conditions = ConditionsBuilder::new(Duration::from_millis(100))
                                      .first_opens(true)
                                      .patterns(vec!["PATTERN_NAME1".to_string(),
                                                     "PATTERN_NAME2".to_string(),
                                                     "f13dafee-cd14-4dda-995c-6ed476a21de3"
                                                         .to_string()])
                                      .build();
        let message = Template::compile("message".to_string())
                          .ok()
                          .expect("Failed to compile a handlebars template");
        let expected_exec_cond = ExecCondition {
            on_opened: false,
            on_closed: true,
        };
        let expected_actions = vec![ActionType::Message(MessageActionBuilder::new("uuid1",
                                                                                message)
                                                            .when(expected_exec_cond)
                                                            .build())];
        let context = result.ok().expect("Failed to deserialize a valid Context");
        assert_eq!(&Some(expected_name), &context.name);
        assert_eq!(&expected_uuid, &context.uuid);
        assert_eq!(&expected_conditions, &context.conditions);
        assert_eq!(&expected_actions, &context.actions);
    }

    #[test]
    fn test_given_config_context_when_it_is_deserialized_and_only_the_required_fields_are_present_then_we_can_deserialize_it_successfully
        () {
        let text = r#"
        {
            "uuid": "86ca9f93-84fb-4813-b037-6526f7a585a3",
            "conditions": {
                "timeout": 100
            }
        }
        "#;

        let result = from_str::<Context>(text);
        println!("{:?}", &result);
        let expected_uuid = Uuid::parse_str("86ca9f93-84fb-4813-b037-6526f7a585a3").ok().unwrap();
        let expected_conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let context = result.ok().expect("Failed to deserialize a valid Context");
        assert_eq!(&expected_uuid, &context.uuid);
        assert_eq!(&expected_conditions, &context.conditions);
    }

    #[test]
    fn test_given_config_context_when_it_is_deserialized_and_the_uuid_is_invalid_then_we_report_an_error
        () {
        let text = r#"
        {
            "uuid": "this is an invalid uuid",
            "conditions": {
                "timeout": 100
            }
        }
        "#;

        let result = from_str::<Context>(text);
        println!("{:?}", &result);
        let _ = result.err()
                      .expect("Successfully deserialized an invalid Context (UUID is invalid)");
    }

    #[test]
    fn test_given_config_context_when_it_contains_context_id_then_can_be_deserialized() {
        let text = r#"
        {
            "uuid": "86ca9f93-84fb-4813-b037-6526f7a585a3",
            "context_id": "{{HOST}}{{PROGRAM}}",
            "conditions": {
                "timeout": 100
            }
        }
        "#;
        let expected_context_id = "{{HOST}}{{PROGRAM}}".to_string();
        let result = from_str::<Context>(text);
        println!("{:?}", &result);
        let context = result.ok().expect("Failed to deserialize a valid Context");
        assert_eq!(&expected_context_id,
                   &context.context_id.as_ref().unwrap().to_string());
    }
}
<|endoftext|>