Skip to content
New issue

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

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

Already on GitHub? Sign in 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants