Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add core::convert::TryInto<Primitive> implementation for Expr #604

Closed
MauriceKayser opened this issue Mar 11, 2019 · 1 comment
Closed

Comments

@MauriceKayser
Copy link

MauriceKayser commented Mar 11, 2019

In several procedural macro crates I need to calculate the value of numeric enumeration variants. Currently I copy over the function every time, but I can not imagine that I am the only user of the syn crate, that would benefit from an integrated variant.

I do not know how feasible it is to have this in syn, because afaik core::convert::TryInto is nightly only, as of now.

I am not too experienced with Rust yet, and not sure how correct and complete my implementation is, as the enumeration variant expressions I use are not too complicated normally, but this is somewhat in the direction I would need (does not account for float values):

impl core::convert::TryInto<i8> for &Expr {
    type Error = ();

    fn try_into(self) -> core::result::Result<i8, Self::Error> {
        Expr::try_into_signed::<i8>(&self)
    }
}

impl core::convert::TryInto<i16> for &Expr {
    type Error = ();

    fn try_into(self) -> core::result::Result<i16, Self::Error> {
        Expr::try_into_signed::<i16>(&self)
    }
}

impl core::convert::TryInto<i32> for &Expr {
    type Error = ();

    fn try_into(self) -> core::result::Result<i32, Self::Error> {
        Expr::try_into_signed::<i32>(&self)
    }
}

impl core::convert::TryInto<i64> for &Expr {
    type Error = ();

    fn try_into(self) -> core::result::Result<i64, Self::Error> {
        Expr::try_into_signed::<i64>(&self)
    }
}

impl core::convert::TryInto<i128> for &Expr {
    type Error = ();

    fn try_into(self) -> core::result::Result<i128, Self::Error> {
        Expr::try_into_signed::<i128>(&self)
    }
}

impl core::convert::TryInto<u8> for &Expr {
    type Error = ();

    fn try_into(self) -> core::result::Result<u8, Self::Error> {
        Expr::try_into_unsigned::<u8>(&self)
    }
}

impl core::convert::TryInto<u16> for &Expr {
    type Error = ();

    fn try_into(self) -> core::result::Result<u16, Self::Error> {
        Expr::try_into_unsigned::<u16>(&self)
    }
}

impl core::convert::TryInto<u32> for &Expr {
    type Error = ();

    fn try_into(self) -> core::result::Result<u32, Self::Error> {
        Expr::try_into_unsigned::<u32>(&self)
    }
}

impl core::convert::TryInto<u64> for &Expr {
    type Error = ();

    fn try_into(self) -> core::result::Result<u64, Self::Error> {
        Expr::try_into_unsigned::<u64>(&self)
    }
}

impl core::convert::TryInto<u128> for &Expr {
    type Error = ();

    fn try_into(self) -> core::result::Result<u128, Self::Error> {
        Expr::try_into_unsigned::<u128>(&self)
    }
}

impl Expr {
    fn try_into_signed<T>(&self) -> core::result::Result<T, ()>
        where T: Sized +
        // Binary operations
        core::ops::Add<Output=T> +
        core::ops::Sub<Output=T> +
        core::ops::Mul<Output=T> +
        core::ops::Div<Output=T> +
        core::ops::Rem<Output=T> +
        core::ops::BitXor<Output=T> +
        core::ops::BitAnd<Output=T> +
        core::ops::BitOr<Output=T> +
        core::ops::Shl<Output=T> +
        core::ops::Shr<Output=T> +

        // Unary operations
        core::ops::Not<Output=T> +
        core::ops::Neg<Output=T> +

        // Literal conversions
        core::convert::TryFrom<u8> +
        core::convert::TryFrom<u64>
    {
        match self {
            Expr::Binary(expr_binary) => {
                let left: T = Self::try_into_signed(&expr_binary.left)?;
                let right: T = Self::try_into_signed(&expr_binary.right)?;

                match expr_binary.op {
                    BinOp::Add(_) => Ok(left + right),
                    BinOp::Sub(_) => Ok(left - right),
                    BinOp::Mul(_) => Ok(left * right),
                    BinOp::Div(_) => Ok(left / right),
                    BinOp::Rem(_) => Ok(left % right),
                    BinOp::BitXor(_) => Ok(left ^ right),
                    BinOp::BitAnd(_) => Ok(left & right),
                    BinOp::BitOr(_) => Ok(left | right),
                    BinOp::Shl(_) => Ok(left << right),
                    BinOp::Shr(_) => Ok(left >> right),

                    _ => Err(())
                }
            },

            Expr::Unary(expr_unary) => {
                let expr: T = Self::try_into_signed(&expr_unary.expr)?;

                match expr_unary.op {
                    UnOp::Not(_) => Ok(!expr),
                    UnOp::Neg(_) => Ok(-expr),

                    _ => Err(())
                }
            },

            Expr::Lit(lit) => {
                match &lit.lit {
                    Lit::Byte(value) => core::convert::TryInto::<T>::try_into(value.value()).map_err(|_| ()),
                    Lit::Int(value) => core::convert::TryInto::<T>::try_into(value.value()).map_err(|_| ()),

                    _ => Err(())
                }
            },

            Expr::Paren(expr) => Self::try_into_signed(&expr.expr),

            _ => Err(())
        }
    }

    fn try_into_unsigned<T>(&self) -> core::result::Result<T, ()>
        where T: Sized +
        // Binary operations
        core::ops::Add<Output=T> +
        core::ops::Sub<Output=T> +
        core::ops::Mul<Output=T> +
        core::ops::Div<Output=T> +
        core::ops::Rem<Output=T> +
        core::ops::BitXor<Output=T> +
        core::ops::BitAnd<Output=T> +
        core::ops::BitOr<Output=T> +
        core::ops::Shl<Output=T> +
        core::ops::Shr<Output=T> +

        // Unary operations
        core::ops::Not<Output=T> +
        // core::ops::Neg<Output=T> +

        // Literal conversions
        core::convert::TryFrom<u8> +
        core::convert::TryFrom<u64>
    {
        match self {
            Expr::Binary(expr_binary) => {
                let left: T = Self::try_into_unsigned(&expr_binary.left)?;
                let right: T = Self::try_into_unsigned(&expr_binary.right)?;

                match expr_binary.op {
                    BinOp::Add(_) => Ok(left + right),
                    BinOp::Sub(_) => Ok(left - right),
                    BinOp::Mul(_) => Ok(left * right),
                    BinOp::Div(_) => Ok(left / right),
                    BinOp::Rem(_) => Ok(left % right),
                    BinOp::BitXor(_) => Ok(left ^ right),
                    BinOp::BitAnd(_) => Ok(left & right),
                    BinOp::BitOr(_) => Ok(left | right),
                    BinOp::Shl(_) => Ok(left << right),
                    BinOp::Shr(_) => Ok(left >> right),

                    _ => Err(())
                }
            },

            Expr::Unary(expr_unary) => {
                let expr: T = Self::try_into_unsigned(&expr_unary.expr)?;

                match expr_unary.op {
                    UnOp::Not(_) => Ok(!expr),
                    // UnOp::Neg(_) => Ok(-expr),

                    _ => Err(())
                }
            },

            Expr::Lit(lit) => {
                match &lit.lit {
                    Lit::Byte(value) => core::convert::TryInto::<T>::try_into(value.value()).map_err(|_| ()),
                    Lit::Int(value) => core::convert::TryInto::<T>::try_into(value.value()).map_err(|_| ()),

                    _ => Err(())
                }
            },

            Expr::Paren(expr) => Self::try_into_unsigned(&expr.expr),

            _ => Err(())
        }
    }
}
@dtolnay
Copy link
Owner

dtolnay commented Mar 12, 2019

Neat! Thanks for the suggestion and example implementation.

I am closing the issue because I would prefer for this to be developed outside of Syn. Syn is a parsing library and I don't think expanding into other areas of compilation beyond parsing like name resolution or const evaluation would be a good idea.

@dtolnay dtolnay closed this as completed Mar 12, 2019
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants