<commit_before>//! A module providing an enum for a message prefix.
use std::borrow::ToOwned;
use std::string;
use std::str::FromStr;
use std::fmt;

/// The Prefix indicates "the true origin of the message", according to the server.
/// It will
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum Prefix {
    /// servername
    ServerName(String),
    /// nickname [ ["!" username] "@" hostname ]
    Nickname(String, Option<String>, Option<String>),
}

impl Prefix {
    /// Creates a prefix by parsing a string.
    ///
    /// #Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// Prefix::new_from_str("nickname!username@hostname");
    /// Prefix::new_from_str("example.com");
    /// # }
    /// ```
    pub fn new_from_str(s: &str) -> Prefix {
        let mut name = String::new();
        let mut user = None;
        let mut host = None;

        for c in s.chars() {
            match c {
                '.' if user.is_none() && host.is_none() => return Prefix::ServerName(s.to_owned()),
                '!' if user.is_none() => {
                    user = Some(String::new())
                },
                '@' if user.is_some() && host.is_none() => {
                    host = Some(String::new())
                },
                _ => {
                    if host.is_some() {
                        host.as_mut().unwrap().push(c)
                    } else if user.is_some() {
                        user.as_mut().unwrap().push(c)
                    } else {
                        name.push(c)
                    }
                }
            }
        }

        Prefix::Nickname(name, user, host)
    }
}

impl FromStr for Prefix {
    type Err = string::ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Prefix::new_from_str(s))
    }
}

impl fmt::Display for Prefix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Prefix::ServerName(name) => write!(f, "{}", name),
            Prefix::Nickname(name, None, None) => write!(f, "{}", name),
            Prefix::Nickname(name, Some(user), None) => write!(f, "{}!{}", name, user),
            Prefix::Nickname(name, Some(user), Some(host)) => write!(f, "{}!{}@{}", name, user, host),
            // This is an issue with the type using two Option values when really host implies user
            // Maybe this should do the same as the (name, None, None) case
            Prefix::Nickname(_, None, Some(_)) => panic!("can't display prefix with host but not username"),
        }
    }
}

impl<'a> From<&'a str> for Prefix {
    fn from(s: &str) -> Self {
        Prefix::new_from_str(s)
    }
}

#[cfg(test)]
mod test {
    use super::Prefix::{self, ServerName, Nickname};

    // Checks that str -> parsed -> Display doesn't lose data
    fn test_parse(s: &str) -> Prefix {
        let prefix = Prefix::new_from_str(s);
        let s2 = format!("{}", prefix);
        assert_eq!(s, &s2);
        prefix
    }

    #[test]
    fn parse_word() {
        assert_eq!(
            test_parse("only_nick"),
            Nickname("only_nick".into(), None, None)
        )
    }

    #[test]
    fn parse_host() {
        assert_eq!(
            test_parse("host.tld"),
            ServerName("host.tld".into())
        )
    }

    #[test]
    fn parse_nick_user() {
        assert_eq!(
            test_parse("test!nick"),
            Nickname("test".into(), Some("nick".into()), None)
        )
    }

    #[test]
    fn parse_nick_user_host() {
        assert_eq!(
            test_parse("test!nick@host"),
            Nickname("test".into(), Some("nick".into()), Some("host".into()))
        )
    }

    #[test]
    fn parse_dot_and_symbols() {
        assert_eq!(
            test_parse("test.net@something"),
            ServerName("test.net@something".into())
        )
    }

    #[test]
    fn parse_danger_cases() {
        assert_eq!(
            test_parse("name@name!user"),
            Nickname("name@name".into(), Some("user".into()), None)
        );
        assert_eq!(
            test_parse("name!@"),
            Nickname("name".into(), Some("".into()), Some("".into()))
        );
        assert_eq!(
            test_parse("name!.user"),
            Nickname("name".into(), Some(".user".into()), None)
        );
        assert_eq!(
            test_parse("name!user.user"),
            Nickname("name".into(), Some("user.user".into()), None)
        );
        assert_eq!(
            test_parse("name!user@host.host"),
            Nickname("name".into(), Some("user".into()), Some("host.host".into()))
        );
        assert_eq!(
            test_parse("!user"),
            Nickname("".into(), Some("user".into()), None)
        );
        assert_eq!(
            test_parse("!@host.host"),
            Nickname("".into(), Some("".into()), Some("host.host".into()))
        );
    }
}
<commit_msg>prefix cleanup and docs<commit_after>//! A module providing an enum for a message prefix.
use std::borrow::ToOwned;
use std::string;
use std::str::FromStr;
use std::fmt;

/// The Prefix indicates "the true origin of the message", according to the server.
///
/// Warning: Avoid constructing a `Nickname(nickname, None, Some(hostname))`, but
/// `Nickname(nickname, Some("".to_owned()), Some(hostname))` works reliably.
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum Prefix {
    /// servername
    ServerName(String),
    /// nickname [ ["!" username] "@" hostname ]
    Nickname(String, Option<String>, Option<String>),
}

impl Prefix {
    /// Creates a prefix by parsing a string.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// Prefix::new_from_str("nickname!username@hostname");
    /// Prefix::new_from_str("example.com");
    /// # }
    /// ```
    pub fn new_from_str(s: &str) -> Prefix {
        let mut name = String::new();
        let mut user = None;
        let mut host = None;

        for c in s.chars() {
            match c {
                // We consider the '.' to be a ServerName except if a ! has already
                // been encountered.
                '.' if user.is_none() => return Prefix::ServerName(s.to_owned()),

                '!' if user.is_none() => {
                    user = Some(String::new())
                },

                // The '@' is not special until we've started the username
                // portion
                '@' if user.is_some() && host.is_none() => {
                    host = Some(String::new())
                },

                _ => {
                    // Push onto the latest buffer
                    if host.is_some() {
                        host.as_mut().unwrap().push(c)
                    } else if user.is_some() {
                        user.as_mut().unwrap().push(c)
                    } else {
                        name.push(c)
                    }
                }
            }
        }

        Prefix::Nickname(name, user, host)
    }
}

/// This implementation never returns an error and is isomorphic with `Display`.
impl FromStr for Prefix {
    type Err = string::ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Prefix::new_from_str(s))
    }
}

/// This is isomorphic with `FromStr`, except when called on `Prefix::Nickname(nickname, None, Some(hostname))`
impl fmt::Display for Prefix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Prefix::ServerName(name) => write!(f, "{}", name),
            Prefix::Nickname(name, None, None) => write!(f, "{}", name),
            Prefix::Nickname(name, Some(user), None) => write!(f, "{}!{}", name, user),
            Prefix::Nickname(name, Some(user), Some(host)) => write!(f, "{}!{}@{}", name, user, host),

            // This is an issue with the type using two Option values when really hostname implies username
            Prefix::Nickname(name, None, Some(host)) => write!(f, "{}!@{}", name, host),
        }
    }
}

impl<'a> From<&'a str> for Prefix {
    fn from(s: &str) -> Self {
        Prefix::new_from_str(s)
    }
}

#[cfg(test)]
mod test {
    use super::Prefix::{self, ServerName, Nickname};

    // Checks that str -> parsed -> Display doesn't lose data
    fn test_parse(s: &str) -> Prefix {
        let prefix = Prefix::new_from_str(s);
        let s2 = format!("{}", prefix);
        assert_eq!(s, &s2);
        prefix
    }

    #[test]
    fn parse_word() {
        assert_eq!(
            test_parse("only_nick"),
            Nickname("only_nick".into(), None, None)
        )
    }

    #[test]
    fn parse_host() {
        assert_eq!(
            test_parse("host.tld"),
            ServerName("host.tld".into())
        )
    }

    #[test]
    fn parse_nick_user() {
        assert_eq!(
            test_parse("test!nick"),
            Nickname("test".into(), Some("nick".into()), None)
        )
    }

    #[test]
    fn parse_nick_user_host() {
        assert_eq!(
            test_parse("test!nick@host"),
            Nickname("test".into(), Some("nick".into()), Some("host".into()))
        )
    }

    #[test]
    fn parse_dot_and_symbols() {
        assert_eq!(
            test_parse("test.net@something"),
            ServerName("test.net@something".into())
        )
    }

    #[test]
    fn parse_danger_cases() {
        assert_eq!(
            test_parse("name@name!user"),
            Nickname("name@name".into(), Some("user".into()), None)
        );
        assert_eq!(
            test_parse("name!@"),
            Nickname("name".into(), Some("".into()), Some("".into()))
        );
        assert_eq!(
            test_parse("name!@hostname"),
            Nickname("name".into(), Some("".into()), Some("hostname".into()))
        );
        assert_eq!(
            test_parse("name!.user"),
            Nickname("name".into(), Some(".user".into()), None)
        );
        assert_eq!(
            test_parse("name!user.user"),
            Nickname("name".into(), Some("user.user".into()), None)
        );
        assert_eq!(
            test_parse("name!user@host.host"),
            Nickname("name".into(), Some("user".into()), Some("host.host".into()))
        );
        assert_eq!(
            test_parse("!user"),
            Nickname("".into(), Some("user".into()), None)
        );
        assert_eq!(
            test_parse("!@host.host"),
            Nickname("".into(), Some("".into()), Some("host.host".into()))
        );
    }
}
<|endoftext|>


<commit_before>#[macro_use]
mod macros;

use internal::IResult;
use error::ParseError;

pub fn pair<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, (O1, O2), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    second(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn pairc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, (O1, O2), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  pair(first, second)(input)
}

pub fn preceded<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, _) = first(input)?;
    second(input)
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn precededc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  preceded(first, second)(input)
}

pub fn terminated<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, O1, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    second(input).map(|(i, _)| (i, o1))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn terminatedc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, O1, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  terminated(first, second)(input)
}

pub fn separated_pair<I, O1, O2, O3, E: ParseError<I>, F, G, H>(first: F, sep: G, second: H) -> impl Fn(I) -> IResult<I, (O1, O3), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    let (input, _) = sep(input)?;
    second(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn separated_pairc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(input: I, first: F, sep: G, second: H) -> IResult<I, (O1, O3), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  separated_pair(first, sep, second)(input)
}

pub fn delimited<I, O1, O2, O3, E: ParseError<I>, F, G, H>(first: F, sep: G, second: H) -> impl Fn(I) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  move |input: I| {
    let (input, _) = first(input)?;
    let (input, o2) = sep(input)?;
    second(input).map(|(i, _)| (i, o2))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn delimitedc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(input: I, first: F, sep: G, second: H) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  delimited(first, sep, second)(input)
}
<commit_msg>add the tuple combinator<commit_after>#[macro_use]
mod macros;

use internal::IResult;
use error::ParseError;

pub fn pair<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, (O1, O2), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    second(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn pairc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, (O1, O2), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  pair(first, second)(input)
}

pub fn preceded<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, _) = first(input)?;
    second(input)
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn precededc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  preceded(first, second)(input)
}

pub fn terminated<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, O1, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    second(input).map(|(i, _)| (i, o1))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn terminatedc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, O1, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  terminated(first, second)(input)
}

pub fn separated_pair<I, O1, O2, O3, E: ParseError<I>, F, G, H>(first: F, sep: G, second: H) -> impl Fn(I) -> IResult<I, (O1, O3), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    let (input, _) = sep(input)?;
    second(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn separated_pairc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(input: I, first: F, sep: G, second: H) -> IResult<I, (O1, O3), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  separated_pair(first, sep, second)(input)
}

pub fn delimited<I, O1, O2, O3, E: ParseError<I>, F, G, H>(first: F, sep: G, second: H) -> impl Fn(I) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  move |input: I| {
    let (input, _) = first(input)?;
    let (input, o2) = sep(input)?;
    second(input).map(|(i, _)| (i, o2))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn delimitedc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(input: I, first: F, sep: G, second: H) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  delimited(first, sep, second)(input)
}

pub trait Tuple<I,O,E> {
  fn parse(&self, input: I) -> IResult<I,O,E>;
}

macro_rules! tuple_parser(
  ($name1:ident $ty1:ident, $name2: ident $ty2:ident, $($name:ident $ty:ident),*) => (
    tuple_parser!(__impl $name1 $ty1, $name2 $ty2; $($name $ty),*);
  );
  (__impl $($name:ident $ty: ident),+; $name1:ident $ty1:ident, $($name2:ident $ty2:ident),*) => (
    tuple_impl!($($name $ty),+);
    tuple_parser!(__impl $($name $ty),+ , $name1 $ty1; $($name2 $ty2),*);
  );
  (__impl $($name:ident $ty: ident),+; $name1:ident $ty1:ident) => (
    tuple_impl!($($name $ty),+);
    tuple_impl!($($name $ty),+, $name1 $ty1);
  );
);

macro_rules! tuple_impl(
  ($($name:ident $ty: ident),+) => (
    impl<
      Input: Clone, $($ty),+ , Error: ParseError<Input>,
      $($name: Fn(Input) -> IResult<Input, $ty, Error>),+
    > Tuple<Input, ( $($ty),+ ), Error> for ( $($name),+ ) {

      fn parse(&self, input: Input) -> IResult<Input, ( $($ty),+ ), Error> {
        tuple_inner!(0, self, input, (), $($name)+)

      }
    }
  );
);

macro_rules! tuple_inner(
  ($it:tt, $self:expr, $input:expr, (), $head:ident $($id:ident)+) => ({
    let (i, o) = acc!($it, $self)($input.clone())?;

    succ!($it, tuple_inner!($self, i, ( o ), $($id)+))
  });
  ($it:tt, $self:expr, $input:expr, ($($parsed:tt)*), $head:ident $($id:ident)+) => ({
    let (i, o) = acc!($it, $self)($input.clone())?;

    succ!($it, tuple_inner!($self, i, ($($parsed)* , o), $($id)+))
  });
  ($it:tt, $self:expr, $input:expr, ($($parsed:tt)*), $head:ident) => ({
    let (i, o) = acc!($it, $self)($input.clone())?;

    Ok((i, ($($parsed)* , o)))
  });
);

tuple_parser!(FnA A, FnB B, FnC C, FnD D, FnE E, FnF F, FnG G, FnH H, FnI I, FnJ J, FnK K, FnL L,
  FnM M, FnN N, FnO O, FnP P, FnQ Q, FnR R, FnS S, FnT T, FnU U);

pub fn tuple<I: Clone, O, E: ParseError<I>, List: Tuple<I,O,E>>(l: List)  -> impl Fn(I) -> IResult<I, O, E> {
  move |i: I| {
    l.parse(i)
  }
}
<|endoftext|>


<commit_before>
extern crate ndarray;
extern crate ndarray_odeint;
extern crate ndarray_linalg;
extern crate itertools;
extern crate num_traits;

use std::fs::*;
use std::io::Write;
use ndarray::prelude::*;
use ndarray_linalg::prelude::*;
use itertools::iterate;
use ndarray_odeint::lorenz63 as l63;
use num_traits::int::PrimInt;

type V = Array<f64, Ix1>;

macro_rules! impl_accuracy_test {
    ($name:ident, $method:path, $filename:expr) => {
#[test]
fn $name() {
    let p = l63::default_parameter();
    let l = |y| l63::f(p, y);
    let data: Vec<_> = (0..12)
        .map(|n| {
            let dt = 0.1 / 2.pow(n) as f64;
            let t = 100 * 2.pow(n);
            let ts = iterate(arr1(&[1.0, 0.0, 0.0]), |y| $method(&l, dt, y.clone()));
            (dt, ts.take(t+1).last().unwrap())
        })
        .collect();

    let mut f = File::create($filename).unwrap();
    write!(&mut f, "dt,dev\n").unwrap();
    for i in 0..(data.len() - 1) {
        let dt = data[i].0;
        let dev = (&data[i + 1].1 - &data[i].1).norm();
        write!(&mut f, "{},{}\n", dt, dev).unwrap();
    }
}
}} // impl_accuracy_test

impl_accuracy_test!(euler, ndarray_odeint::explicit::euler, "euler.csv");
impl_accuracy_test!(rk4, ndarray_odeint::explicit::rk4, "rk4.csv");
<commit_msg>default_parameter -> Parameter::default in test<commit_after>
extern crate ndarray;
extern crate ndarray_odeint;
extern crate ndarray_linalg;
extern crate itertools;
extern crate num_traits;

use std::fs::*;
use std::io::Write;
use ndarray::prelude::*;
use ndarray_linalg::prelude::*;
use itertools::iterate;
use ndarray_odeint::lorenz63 as l63;
use num_traits::int::PrimInt;

type V = Array<f64, Ix1>;

macro_rules! impl_accuracy_test {
    ($name:ident, $method:path, $filename:expr) => {
#[test]
fn $name() {
    let p = l63::Parameter::default();
    let l = |y| l63::f(p, y);
    let data: Vec<_> = (0..12)
        .map(|n| {
            let dt = 0.1 / 2.pow(n) as f64;
            let t = 100 * 2.pow(n);
            let ts = iterate(arr1(&[1.0, 0.0, 0.0]), |y| $method(&l, dt, y.clone()));
            (dt, ts.take(t+1).last().unwrap())
        })
        .collect();

    let mut f = File::create($filename).unwrap();
    write!(&mut f, "dt,dev\n").unwrap();
    for i in 0..(data.len() - 1) {
        let dt = data[i].0;
        let dev = (&data[i + 1].1 - &data[i].1).norm();
        write!(&mut f, "{},{}\n", dt, dev).unwrap();
    }
}
}} // impl_accuracy_test

impl_accuracy_test!(euler, ndarray_odeint::explicit::euler, "euler.csv");
impl_accuracy_test!(rk4, ndarray_odeint::explicit::rk4, "rk4.csv");
<|endoftext|>


<commit_before>//! Contains types that represent uniform data, which is used
//! for methods such as [`gl.set_uniform`]
//! (../context/program_context/trait.ContextProgramExt.html#method.set_uniform).

use std::slice;
use std::mem;

/// The basic value types that are composed in the [`UniformDatumTypes`]
/// (enum.UniformDatumType.html).
pub enum UniformPrimitiveType {
    /// A 32-bit floating point value.
    Float,

    /// A 32-bit signed integer value.
    Int
}

/// The basic types that can be used as uniform values in a program object.
pub enum UniformDatumType {
    /// A single scalar value, containing one primitive (essentially a vector
    /// of one component).
    Vec1(UniformPrimitiveType),

    /// A vector with 2 components.
    Vec2(UniformPrimitiveType),

    /// A vector with 3 components.
    Vec3(UniformPrimitiveType),

    /// A vector with 4 components.
    Vec4(UniformPrimitiveType),

    /// A 2x2 matrix of floating-point values.
    Matrix2x2,

    /// A 3x3 matrix of floating-point values.
    Matrix3x3,

    /// A 4x4 matrix of floating-point values.
    Matrix4x4
}

/// A type that can be set to a uniform value in a program object, using
/// the [`gl.set_uniform`]
/// (../context/program_context/trait.ContextProgramExt.html#method.set_uniform)
/// method. A `UniformData` type can be composed of one or more
/// [`UniformDatums`](trait.UniformDatum.html), and this is likely the type
/// that user types will implement.
pub trait UniformData {
    /// Return the type that this uniform data should be treated as.
    fn uniform_datum_type() -> UniformDatumType;

    /// Create a byte slice of uniform data from `self`.
    fn uniform_bytes(&self) -> &[u8];

    /// Return the number of uniform data elements that `self` contains.
    fn uniform_elements(&self) -> usize;
}

/// A single uniform value, which corresponds to a single
/// primitive GLSL uniform type.
///
/// # Safety
/// This type will be transmuted to a slice according to the value
/// returned by the [`uniform_datum_type`]
/// (trait.UniformDatum.html#tymethod.uniform_datum_type)
/// method. Implementing this method incorrectly will lead to memory
/// unsafety.
pub unsafe trait UniformDatum {
    /// Return the data type this datum corresponds to.
    ///
    /// # Safety
    /// An instance of this type must match the size and memory layout
    /// specified by the returned [`UniformDatumType`]
    /// (enum.UniformDatumType.html).
    fn uniform_datum_type() -> UniformDatumType;
}

/// A single, basic value that can be composed to make a [`UniformDatum`]
/// (trait.UniformDatum.html). Scalar values are an example of a `UniformPrimitive`.
///
/// # Safety
/// This type will be transmuted to a slice according to the value returned
/// by the [`uniform_primitive_type`]
/// (trait.UniformPrimitive.html#tymethod.uniform_primitive_type) method.
/// Implementing this method incorrectly will lead to memory unsafety.
pub unsafe trait UniformPrimitive {
    /// Return the data type this primitive corresponds to.
    ///
    /// # Safety
    /// An instance of this type must be the size of the
    /// [`UniformPrimitiveType`](enum.UniformPrimitiveType.html)
    /// returned by this function.
    fn uniform_primitive_type() -> UniformPrimitiveType;
}

unsafe impl UniformPrimitive for f32 {
    fn uniform_primitive_type() -> UniformPrimitiveType {
        UniformPrimitiveType::Float
    }
}

unsafe impl UniformPrimitive for i32 {
    fn uniform_primitive_type() -> UniformPrimitiveType {
        UniformPrimitiveType::Int
    }
}

unsafe impl<T: UniformPrimitive> UniformDatum for T {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Vec1(T::uniform_primitive_type())
    }
}

unsafe impl<T: UniformPrimitive> UniformDatum for [T; 1] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Vec1(T::uniform_primitive_type())
    }
}

unsafe impl<T: UniformPrimitive> UniformDatum for [T; 2] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Vec1(T::uniform_primitive_type())
    }
}

unsafe impl<T: UniformPrimitive> UniformDatum for [T; 3] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Vec1(T::uniform_primitive_type())
    }
}

unsafe impl<T> UniformDatum for [T; 4] where T: UniformPrimitive {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Vec1(T::uniform_primitive_type())
    }
}

unsafe impl UniformDatum for [[f32; 2]; 2] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Matrix2x2
    }
}

unsafe impl UniformDatum for [[f32; 3]; 3] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Matrix3x3
    }
}

unsafe impl UniformDatum for [[f32; 4]; 4] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Matrix4x4
    }
}

impl<T: UniformDatum> UniformData for T {
    fn uniform_datum_type() -> UniformDatumType {
        T::uniform_datum_type()
    }

    fn uniform_bytes(&self) -> &[u8] {
        unsafe {
            slice::from_raw_parts(mem::transmute(self), mem::size_of::<T>())
        }
    }

    fn uniform_elements(&self) -> usize {
        1
    }
}

impl<T: UniformDatum> UniformData for [T] {
    fn uniform_datum_type() -> UniformDatumType {
        T::uniform_datum_type()
    }

    fn uniform_bytes(&self) -> &[u8] {
        let size = mem::size_of::<T>() * self.len();
        unsafe {
            slice::from_raw_parts(mem::transmute(&self[0]), size)
        }
    }

    fn uniform_elements(&self) -> usize {
        self.len()
    }
}

/// Transpose a static matrix (of form `[[a1, b1, ...], [a2, b2, ...]]`
/// into `[[a1, a2, ...], [[b1, b2, ...]]]`).
macro_rules! transpose {
    [[$a1:expr, $b1:expr],
     [$a2:expr, $b2:expr]] => {
        [[$a1, $a2],
         [$b1, $b2]]
    };
    [[$a1:expr, $b1:expr, $c1:expr],
     [$a2:expr, $b2:expr, $c2:expr],
     [$a3:expr, $b3:expr, $c3:expr]] => {
        [[$a1, $a2, $a3],
         [$b1, $b2, $b3],
         [$c1, $c2, $c3]]
    };
    [[$a1:expr, $b1:expr, $c1:expr, $d1:expr],
     [$a2:expr, $b2:expr, $c2:expr, $d2:expr],
     [$a3:expr, $b3:expr, $c3:expr, $d3:expr],
     [$a4:expr, $b4:expr, $c4:expr, $d4:expr]] => {
        [[$a1, $a2, $a3, $a4],
         [$b1, $b2, $b3, $b4],
         [$c1, $c2, $c3, $c4],
         [$d1, $d2, $d3, $d4]]
    }
}
<commit_msg>Remove `transpose!` macro<commit_after>//! Contains types that represent uniform data, which is used
//! for methods such as [`gl.set_uniform`]
//! (../context/program_context/trait.ContextProgramExt.html#method.set_uniform).

use std::slice;
use std::mem;

/// The basic value types that are composed in the [`UniformDatumTypes`]
/// (enum.UniformDatumType.html).
pub enum UniformPrimitiveType {
    /// A 32-bit floating point value.
    Float,

    /// A 32-bit signed integer value.
    Int
}

/// The basic types that can be used as uniform values in a program object.
pub enum UniformDatumType {
    /// A single scalar value, containing one primitive (essentially a vector
    /// of one component).
    Vec1(UniformPrimitiveType),

    /// A vector with 2 components.
    Vec2(UniformPrimitiveType),

    /// A vector with 3 components.
    Vec3(UniformPrimitiveType),

    /// A vector with 4 components.
    Vec4(UniformPrimitiveType),

    /// A 2x2 matrix of floating-point values.
    Matrix2x2,

    /// A 3x3 matrix of floating-point values.
    Matrix3x3,

    /// A 4x4 matrix of floating-point values.
    Matrix4x4
}

/// A type that can be set to a uniform value in a program object, using
/// the [`gl.set_uniform`]
/// (../context/program_context/trait.ContextProgramExt.html#method.set_uniform)
/// method. A `UniformData` type can be composed of one or more
/// [`UniformDatums`](trait.UniformDatum.html), and this is likely the type
/// that user types will implement.
pub trait UniformData {
    /// Return the type that this uniform data should be treated as.
    fn uniform_datum_type() -> UniformDatumType;

    /// Create a byte slice of uniform data from `self`.
    fn uniform_bytes(&self) -> &[u8];

    /// Return the number of uniform data elements that `self` contains.
    fn uniform_elements(&self) -> usize;
}

/// A single uniform value, which corresponds to a single
/// primitive GLSL uniform type.
///
/// # Safety
/// This type will be transmuted to a slice according to the value
/// returned by the [`uniform_datum_type`]
/// (trait.UniformDatum.html#tymethod.uniform_datum_type)
/// method. Implementing this method incorrectly will lead to memory
/// unsafety.
pub unsafe trait UniformDatum {
    /// Return the data type this datum corresponds to.
    ///
    /// # Safety
    /// An instance of this type must match the size and memory layout
    /// specified by the returned [`UniformDatumType`]
    /// (enum.UniformDatumType.html).
    fn uniform_datum_type() -> UniformDatumType;
}

/// A single, basic value that can be composed to make a [`UniformDatum`]
/// (trait.UniformDatum.html). Scalar values are an example of a `UniformPrimitive`.
///
/// # Safety
/// This type will be transmuted to a slice according to the value returned
/// by the [`uniform_primitive_type`]
/// (trait.UniformPrimitive.html#tymethod.uniform_primitive_type) method.
/// Implementing this method incorrectly will lead to memory unsafety.
pub unsafe trait UniformPrimitive {
    /// Return the data type this primitive corresponds to.
    ///
    /// # Safety
    /// An instance of this type must be the size of the
    /// [`UniformPrimitiveType`](enum.UniformPrimitiveType.html)
    /// returned by this function.
    fn uniform_primitive_type() -> UniformPrimitiveType;
}

unsafe impl UniformPrimitive for f32 {
    fn uniform_primitive_type() -> UniformPrimitiveType {
        UniformPrimitiveType::Float
    }
}

unsafe impl UniformPrimitive for i32 {
    fn uniform_primitive_type() -> UniformPrimitiveType {
        UniformPrimitiveType::Int
    }
}

unsafe impl<T: UniformPrimitive> UniformDatum for T {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Vec1(T::uniform_primitive_type())
    }
}

unsafe impl<T: UniformPrimitive> UniformDatum for [T; 1] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Vec1(T::uniform_primitive_type())
    }
}

unsafe impl<T: UniformPrimitive> UniformDatum for [T; 2] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Vec1(T::uniform_primitive_type())
    }
}

unsafe impl<T: UniformPrimitive> UniformDatum for [T; 3] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Vec1(T::uniform_primitive_type())
    }
}

unsafe impl<T> UniformDatum for [T; 4] where T: UniformPrimitive {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Vec1(T::uniform_primitive_type())
    }
}

unsafe impl UniformDatum for [[f32; 2]; 2] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Matrix2x2
    }
}

unsafe impl UniformDatum for [[f32; 3]; 3] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Matrix3x3
    }
}

unsafe impl UniformDatum for [[f32; 4]; 4] {
    fn uniform_datum_type() -> UniformDatumType {
        UniformDatumType::Matrix4x4
    }
}

impl<T: UniformDatum> UniformData for T {
    fn uniform_datum_type() -> UniformDatumType {
        T::uniform_datum_type()
    }

    fn uniform_bytes(&self) -> &[u8] {
        unsafe {
            slice::from_raw_parts(mem::transmute(self), mem::size_of::<T>())
        }
    }

    fn uniform_elements(&self) -> usize {
        1
    }
}

impl<T: UniformDatum> UniformData for [T] {
    fn uniform_datum_type() -> UniformDatumType {
        T::uniform_datum_type()
    }

    fn uniform_bytes(&self) -> &[u8] {
        let size = mem::size_of::<T>() * self.len();
        unsafe {
            slice::from_raw_parts(mem::transmute(&self[0]), size)
        }
    }

    fn uniform_elements(&self) -> usize {
        self.len()
    }
}
<|endoftext|>


<commit_before>use crate::{error::WebPushError, vapid::VapidKey};
use base64::{self, URL_SAFE_NO_PAD};
use hyper::Uri;
use openssl::{hash::MessageDigest, pkey::PKey, sign::Signer as SslSigner};
use serde_json::{Number, Value};
use std::collections::BTreeMap;
use time::{self, OffsetDateTime};

lazy_static! {
    static ref JWT_HEADERS: String = base64::encode_config(
        &serde_json::to_string(&json!({
            "typ": "JWT",
            "alg": "ES256"
        }))
        .unwrap(),
        URL_SAFE_NO_PAD
    );
}

/// A struct representing a VAPID signature. Should be generated using the
/// [VapidSignatureBuilder](struct.VapidSignatureBuilder.html).
#[derive(Debug)]
pub struct VapidSignature {
    /// The signature
    pub auth_t: String,
    /// The public key
    pub auth_k: String,
}

impl<'a> ToString for &'a VapidSignature {
    fn to_string(&self) -> String {
        format!("WebPush {}", self.auth_t)
    }
}

pub struct VapidSigner {}

impl VapidSigner {
    /// Create a signature with a given key. Sets the default audience from the
    /// endpoint host and sets the expiry in twelve hours. Values can be
    /// overwritten by adding the `aud` and `exp` claims.
    pub fn sign(
        key: VapidKey,
        endpoint: &Uri,
        mut claims: BTreeMap<&str, Value>,
    ) -> Result<VapidSignature, WebPushError> {
        if !claims.contains_key("aud") {
            let audience = format!("{}://{}", endpoint.scheme_str().unwrap(), endpoint.host().unwrap());
            claims.insert("aud", Value::String(audience));
        }

        if !claims.contains_key("exp") {
            let expiry = OffsetDateTime::now_utc() + time::Duration::hours(12);
            let number = Number::from(expiry.unix_timestamp());
            claims.insert("exp", Value::Number(number));
        }

        let signing_input = format!(
            "{}.{}",
            *JWT_HEADERS,
            base64::encode_config(&serde_json::to_string(&claims)?, URL_SAFE_NO_PAD)
        );

        let public_key = key.public_key();
        let auth_k = base64::encode_config(&public_key, URL_SAFE_NO_PAD);
        let pkey = PKey::from_ec_key(key.0)?;

        let mut signer = SslSigner::new(MessageDigest::sha256(), &pkey)?;
        signer.update(signing_input.as_bytes())?;

        let signature = signer.sign_to_vec()?;

        let r_off: usize = 3;
        let r_len = signature[r_off] as usize;
        let s_off: usize = r_off + r_len + 2;
        let s_len = signature[s_off] as usize;

        let mut r_val = &signature[(r_off + 1)..(r_off + 1 + r_len)];
        let mut s_val = &signature[(s_off + 1)..(s_off + 1 + s_len)];

        if r_len == 33 && r_val[0] == 0 {
            r_val = &r_val[1..];
        }

        if s_len == 33 && s_val[0] == 0 {
            s_val = &s_val[1..];
        }

        let mut sigval: Vec<u8> = Vec::with_capacity(64);
        sigval.extend(r_val);
        sigval.extend(s_val);

        trace!("Public key: {}", auth_k);

        let auth_t = format!("{}.{}", signing_input, base64::encode_config(&sigval, URL_SAFE_NO_PAD));

        Ok(VapidSignature { auth_t, auth_k })
    }
}

#[cfg(test)]
mod tests {
    use crate::vapid::VapidSignature;

    #[test]
    fn test_vapid_signature_aesgcm_format() {
        let vapid_signature = &VapidSignature {
            auth_t: String::from("foo"),
            auth_k: String::from("bar"),
        };

        let header_value: String = vapid_signature.to_string();

        assert_eq!("WebPush foo", &header_value);
    }
}
<commit_msg>Make clippy happy<commit_after>use crate::{error::WebPushError, vapid::VapidKey};
use base64::{self, URL_SAFE_NO_PAD};
use hyper::Uri;
use openssl::{hash::MessageDigest, pkey::PKey, sign::Signer as SslSigner};
use serde_json::{Number, Value};
use std::collections::BTreeMap;
use time::{self, OffsetDateTime};

lazy_static! {
    static ref JWT_HEADERS: String = base64::encode_config(
        &serde_json::to_string(&json!({
            "typ": "JWT",
            "alg": "ES256"
        }))
        .unwrap(),
        URL_SAFE_NO_PAD
    );
}

/// A struct representing a VAPID signature. Should be generated using the
/// [VapidSignatureBuilder](struct.VapidSignatureBuilder.html).
#[derive(Debug)]
pub struct VapidSignature {
    /// The signature
    pub auth_t: String,
    /// The public key
    pub auth_k: String,
}

impl ToString for VapidSignature {
    fn to_string(&self) -> String {
        format!("WebPush {}", self.auth_t)
    }
}

pub struct VapidSigner {}

impl VapidSigner {
    /// Create a signature with a given key. Sets the default audience from the
    /// endpoint host and sets the expiry in twelve hours. Values can be
    /// overwritten by adding the `aud` and `exp` claims.
    pub fn sign(
        key: VapidKey,
        endpoint: &Uri,
        mut claims: BTreeMap<&str, Value>,
    ) -> Result<VapidSignature, WebPushError> {
        if !claims.contains_key("aud") {
            let audience = format!("{}://{}", endpoint.scheme_str().unwrap(), endpoint.host().unwrap());
            claims.insert("aud", Value::String(audience));
        }

        if !claims.contains_key("exp") {
            let expiry = OffsetDateTime::now_utc() + time::Duration::hours(12);
            let number = Number::from(expiry.unix_timestamp());
            claims.insert("exp", Value::Number(number));
        }

        let signing_input = format!(
            "{}.{}",
            *JWT_HEADERS,
            base64::encode_config(&serde_json::to_string(&claims)?, URL_SAFE_NO_PAD)
        );

        let public_key = key.public_key();
        let auth_k = base64::encode_config(&public_key, URL_SAFE_NO_PAD);
        let pkey = PKey::from_ec_key(key.0)?;

        let mut signer = SslSigner::new(MessageDigest::sha256(), &pkey)?;
        signer.update(signing_input.as_bytes())?;

        let signature = signer.sign_to_vec()?;

        let r_off: usize = 3;
        let r_len = signature[r_off] as usize;
        let s_off: usize = r_off + r_len + 2;
        let s_len = signature[s_off] as usize;

        let mut r_val = &signature[(r_off + 1)..(r_off + 1 + r_len)];
        let mut s_val = &signature[(s_off + 1)..(s_off + 1 + s_len)];

        if r_len == 33 && r_val[0] == 0 {
            r_val = &r_val[1..];
        }

        if s_len == 33 && s_val[0] == 0 {
            s_val = &s_val[1..];
        }

        let mut sigval: Vec<u8> = Vec::with_capacity(64);
        sigval.extend(r_val);
        sigval.extend(s_val);

        trace!("Public key: {}", auth_k);

        let auth_t = format!("{}.{}", signing_input, base64::encode_config(&sigval, URL_SAFE_NO_PAD));

        Ok(VapidSignature { auth_t, auth_k })
    }
}

#[cfg(test)]
mod tests {
    use crate::vapid::VapidSignature;

    #[test]
    fn test_vapid_signature_aesgcm_format() {
        let vapid_signature = &VapidSignature {
            auth_t: String::from("foo"),
            auth_k: String::from("bar"),
        };

        let header_value: String = vapid_signature.to_string();

        assert_eq!("WebPush foo", &header_value);
    }
}
<|endoftext|>


<commit_before>use time::macros::offset;
use time::{OffsetDateTime, Result, UtcOffset};

#[test]
fn utc_is_zero() {
    assert_eq!(offset!(UTC), offset!(+0));
}

#[test]
fn from_hms() -> Result<()> {
    assert_eq!(UtcOffset::from_hms(0, 0, 0), Ok(offset!(UTC)));
    assert_eq!(UtcOffset::from_hms(0, 0, 1), Ok(offset!(+0:00:01)));
    assert_eq!(UtcOffset::from_hms(0, 0, -1), Ok(offset!(-0:00:01)));
    assert_eq!(UtcOffset::from_hms(1, 0, 0), Ok(offset!(+1)));
    assert_eq!(UtcOffset::from_hms(-1, 0, 0), Ok(offset!(-1)));
    assert_eq!(UtcOffset::from_hms(23, 59, 0), Ok(offset!(+23:59)));
    assert_eq!(UtcOffset::from_hms(-23, -59, 0), Ok(offset!(-23:59)));
    assert_eq!(UtcOffset::from_hms(23, 59, 59), Ok(offset!(+23:59:59)));
    assert_eq!(UtcOffset::from_hms(-23, -59, -59), Ok(offset!(-23:59:59)));
    assert_eq!(UtcOffset::from_hms(1, 2, 3)?.as_hms(), (1, 2, 3));
    assert_eq!(UtcOffset::from_hms(1, -2, -3)?.as_hms(), (1, 2, 3));
    assert_eq!(UtcOffset::from_hms(0, 2, -3)?.as_hms(), (0, 2, 3));
    Ok(())
}

#[test]
fn from_whole_seconds() -> Result<()> {
    assert_eq!(UtcOffset::from_whole_seconds(0), Ok(offset!(UTC)));
    assert_eq!(UtcOffset::from_whole_seconds(1), Ok(offset!(+0:00:01)));
    assert_eq!(UtcOffset::from_whole_seconds(-1), Ok(offset!(-0:00:01)));
    assert_eq!(UtcOffset::from_whole_seconds(3_600), Ok(offset!(+1)));
    assert_eq!(UtcOffset::from_whole_seconds(-3_600), Ok(offset!(-1)));
    assert_eq!(UtcOffset::from_whole_seconds(86_340), Ok(offset!(+23:59)));
    assert_eq!(UtcOffset::from_whole_seconds(-86_340), Ok(offset!(-23:59)));
    assert_eq!(
        UtcOffset::from_whole_seconds(86_399),
        Ok(offset!(+23:59:59))
    );
    assert_eq!(
        UtcOffset::from_whole_seconds(-86_399),
        Ok(offset!(-23:59:59))
    );
    Ok(())
}

#[test]
fn as_hms() {
    assert_eq!(offset!(UTC).as_hms(), (0, 0, 0));
    assert_eq!(offset!(+0:00:01).as_hms(), (0, 0, 1));
    assert_eq!(offset!(-0:00:01).as_hms(), (0, 0, -1));
    assert_eq!(offset!(+1).as_hms(), (1, 0, 0));
    assert_eq!(offset!(-1).as_hms(), (-1, 0, 0));
    assert_eq!(offset!(+23:59).as_hms(), (23, 59, 0));
    assert_eq!(offset!(-23:59).as_hms(), (-23, -59, 0));
    assert_eq!(offset!(+23:59:59).as_hms(), (23, 59, 59));
    assert_eq!(offset!(-23:59:59).as_hms(), (-23, -59, -59));
}

#[test]
fn whole_hours() {
    assert_eq!(offset!(+1:02:03).whole_hours(), 1);
    assert_eq!(offset!(-1:02:03).whole_hours(), -1);
}

#[test]
fn whole_minutes() {
    assert_eq!(offset!(+1:02:03).whole_minutes(), 62);
    assert_eq!(offset!(-1:02:03).whole_minutes(), -62);
}

#[test]
fn minutes_past_hour() {
    assert_eq!(offset!(+1:02:03).minutes_past_hour(), 2);
    assert_eq!(offset!(-1:02:03).minutes_past_hour(), -2);
}

#[test]
fn whole_seconds() {
    assert_eq!(offset!(UTC).whole_seconds(), 0);
    assert_eq!(offset!(+0:00:01).whole_seconds(), 1);
    assert_eq!(offset!(-0:00:01).whole_seconds(), -1);
    assert_eq!(offset!(+1).whole_seconds(), 3_600);
    assert_eq!(offset!(-1).whole_seconds(), -3_600);
    assert_eq!(offset!(+23:59).whole_seconds(), 86_340);
    assert_eq!(offset!(-23:59).whole_seconds(), -86_340);
    assert_eq!(offset!(+23:59:59).whole_seconds(), 86_399);
    assert_eq!(offset!(-23:59:59).whole_seconds(), -86_399);
}

#[test]
fn seconds_past_minute() {
    assert_eq!(offset!(+1:02:03).seconds_past_minute(), 3);
    assert_eq!(offset!(-1:02:03).seconds_past_minute(), -3);
}

#[test]
fn is_utc() {
    assert!(offset!(UTC).is_utc());
    assert!(!offset!(+0:00:01).is_utc());
    assert!(!offset!(-0:00:01).is_utc());
    assert!(!offset!(+1).is_utc());
    assert!(!offset!(-1).is_utc());
    assert!(!offset!(+23:59).is_utc());
    assert!(!offset!(-23:59).is_utc());
    assert!(!offset!(+23:59:59).is_utc());
    assert!(!offset!(-23:59:59).is_utc());
}

#[test]
fn is_positive() {
    assert!(!offset!(UTC).is_positive());
    assert!(offset!(+0:00:01).is_positive());
    assert!(!offset!(-0:00:01).is_positive());
    assert!(offset!(+1).is_positive());
    assert!(!offset!(-1).is_positive());
    assert!(offset!(+23:59).is_positive());
    assert!(!offset!(-23:59).is_positive());
    assert!(offset!(+23:59:59).is_positive());
    assert!(!offset!(-23:59:59).is_positive());
}

#[test]
fn is_negative() {
    assert!(!offset!(UTC).is_negative());
    assert!(!offset!(+0:00:01).is_negative());
    assert!(offset!(-0:00:01).is_negative());
    assert!(!offset!(+1).is_negative());
    assert!(offset!(-1).is_negative());
    assert!(!offset!(+23:59).is_negative());
    assert!(offset!(-23:59).is_negative());
    assert!(!offset!(+23:59:59).is_negative());
    assert!(offset!(-23:59:59).is_negative());
}

#[test]
fn neg() {
    assert_eq!(-offset!(UTC), offset!(UTC));
    assert_eq!(-offset!(+0:00:01), offset!(-0:00:01));
    assert_eq!(-offset!(-0:00:01), offset!(+0:00:01));
    assert_eq!(-offset!(+1), offset!(-1));
    assert_eq!(-offset!(-1), offset!(+1));
    assert_eq!(-offset!(+23:59), offset!(-23:59));
    assert_eq!(-offset!(-23:59), offset!(+23:59));
    assert_eq!(-offset!(+23:59:59), offset!(-23:59:59));
    assert_eq!(-offset!(-23:59:59), offset!(+23:59:59));
}

#[test]
fn local_offset_at() {
    #[cfg(not(target_family = "unix"))]
    assert!(UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH).is_ok());

    #[cfg(target_family = "unix")]
    let _ = UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH);
}

#[test]
fn current_local_offset() {
    #[cfg(not(target_family = "unix"))]
    assert!(UtcOffset::current_local_offset().is_ok());

    #[cfg(target_family = "unix")]
    let _ = UtcOffset::current_local_offset();
}
<commit_msg>Remove unnecessary `Result` returned<commit_after>use time::macros::offset;
use time::{OffsetDateTime, Result, UtcOffset};

#[test]
fn utc_is_zero() {
    assert_eq!(offset!(UTC), offset!(+0));
}

#[test]
fn from_hms() -> Result<()> {
    assert_eq!(UtcOffset::from_hms(0, 0, 0), Ok(offset!(UTC)));
    assert_eq!(UtcOffset::from_hms(0, 0, 1), Ok(offset!(+0:00:01)));
    assert_eq!(UtcOffset::from_hms(0, 0, -1), Ok(offset!(-0:00:01)));
    assert_eq!(UtcOffset::from_hms(1, 0, 0), Ok(offset!(+1)));
    assert_eq!(UtcOffset::from_hms(-1, 0, 0), Ok(offset!(-1)));
    assert_eq!(UtcOffset::from_hms(23, 59, 0), Ok(offset!(+23:59)));
    assert_eq!(UtcOffset::from_hms(-23, -59, 0), Ok(offset!(-23:59)));
    assert_eq!(UtcOffset::from_hms(23, 59, 59), Ok(offset!(+23:59:59)));
    assert_eq!(UtcOffset::from_hms(-23, -59, -59), Ok(offset!(-23:59:59)));
    assert_eq!(UtcOffset::from_hms(1, 2, 3)?.as_hms(), (1, 2, 3));
    assert_eq!(UtcOffset::from_hms(1, -2, -3)?.as_hms(), (1, 2, 3));
    assert_eq!(UtcOffset::from_hms(0, 2, -3)?.as_hms(), (0, 2, 3));
    Ok(())
}

#[test]
fn from_whole_seconds() {
    assert_eq!(UtcOffset::from_whole_seconds(0), Ok(offset!(UTC)));
    assert_eq!(UtcOffset::from_whole_seconds(1), Ok(offset!(+0:00:01)));
    assert_eq!(UtcOffset::from_whole_seconds(-1), Ok(offset!(-0:00:01)));
    assert_eq!(UtcOffset::from_whole_seconds(3_600), Ok(offset!(+1)));
    assert_eq!(UtcOffset::from_whole_seconds(-3_600), Ok(offset!(-1)));
    assert_eq!(UtcOffset::from_whole_seconds(86_340), Ok(offset!(+23:59)));
    assert_eq!(UtcOffset::from_whole_seconds(-86_340), Ok(offset!(-23:59)));
    assert_eq!(
        UtcOffset::from_whole_seconds(86_399),
        Ok(offset!(+23:59:59))
    );
    assert_eq!(
        UtcOffset::from_whole_seconds(-86_399),
        Ok(offset!(-23:59:59))
    );
}

#[test]
fn as_hms() {
    assert_eq!(offset!(UTC).as_hms(), (0, 0, 0));
    assert_eq!(offset!(+0:00:01).as_hms(), (0, 0, 1));
    assert_eq!(offset!(-0:00:01).as_hms(), (0, 0, -1));
    assert_eq!(offset!(+1).as_hms(), (1, 0, 0));
    assert_eq!(offset!(-1).as_hms(), (-1, 0, 0));
    assert_eq!(offset!(+23:59).as_hms(), (23, 59, 0));
    assert_eq!(offset!(-23:59).as_hms(), (-23, -59, 0));
    assert_eq!(offset!(+23:59:59).as_hms(), (23, 59, 59));
    assert_eq!(offset!(-23:59:59).as_hms(), (-23, -59, -59));
}

#[test]
fn whole_hours() {
    assert_eq!(offset!(+1:02:03).whole_hours(), 1);
    assert_eq!(offset!(-1:02:03).whole_hours(), -1);
}

#[test]
fn whole_minutes() {
    assert_eq!(offset!(+1:02:03).whole_minutes(), 62);
    assert_eq!(offset!(-1:02:03).whole_minutes(), -62);
}

#[test]
fn minutes_past_hour() {
    assert_eq!(offset!(+1:02:03).minutes_past_hour(), 2);
    assert_eq!(offset!(-1:02:03).minutes_past_hour(), -2);
}

#[test]
fn whole_seconds() {
    assert_eq!(offset!(UTC).whole_seconds(), 0);
    assert_eq!(offset!(+0:00:01).whole_seconds(), 1);
    assert_eq!(offset!(-0:00:01).whole_seconds(), -1);
    assert_eq!(offset!(+1).whole_seconds(), 3_600);
    assert_eq!(offset!(-1).whole_seconds(), -3_600);
    assert_eq!(offset!(+23:59).whole_seconds(), 86_340);
    assert_eq!(offset!(-23:59).whole_seconds(), -86_340);
    assert_eq!(offset!(+23:59:59).whole_seconds(), 86_399);
    assert_eq!(offset!(-23:59:59).whole_seconds(), -86_399);
}

#[test]
fn seconds_past_minute() {
    assert_eq!(offset!(+1:02:03).seconds_past_minute(), 3);
    assert_eq!(offset!(-1:02:03).seconds_past_minute(), -3);
}

#[test]
fn is_utc() {
    assert!(offset!(UTC).is_utc());
    assert!(!offset!(+0:00:01).is_utc());
    assert!(!offset!(-0:00:01).is_utc());
    assert!(!offset!(+1).is_utc());
    assert!(!offset!(-1).is_utc());
    assert!(!offset!(+23:59).is_utc());
    assert!(!offset!(-23:59).is_utc());
    assert!(!offset!(+23:59:59).is_utc());
    assert!(!offset!(-23:59:59).is_utc());
}

#[test]
fn is_positive() {
    assert!(!offset!(UTC).is_positive());
    assert!(offset!(+0:00:01).is_positive());
    assert!(!offset!(-0:00:01).is_positive());
    assert!(offset!(+1).is_positive());
    assert!(!offset!(-1).is_positive());
    assert!(offset!(+23:59).is_positive());
    assert!(!offset!(-23:59).is_positive());
    assert!(offset!(+23:59:59).is_positive());
    assert!(!offset!(-23:59:59).is_positive());
}

#[test]
fn is_negative() {
    assert!(!offset!(UTC).is_negative());
    assert!(!offset!(+0:00:01).is_negative());
    assert!(offset!(-0:00:01).is_negative());
    assert!(!offset!(+1).is_negative());
    assert!(offset!(-1).is_negative());
    assert!(!offset!(+23:59).is_negative());
    assert!(offset!(-23:59).is_negative());
    assert!(!offset!(+23:59:59).is_negative());
    assert!(offset!(-23:59:59).is_negative());
}

#[test]
fn neg() {
    assert_eq!(-offset!(UTC), offset!(UTC));
    assert_eq!(-offset!(+0:00:01), offset!(-0:00:01));
    assert_eq!(-offset!(-0:00:01), offset!(+0:00:01));
    assert_eq!(-offset!(+1), offset!(-1));
    assert_eq!(-offset!(-1), offset!(+1));
    assert_eq!(-offset!(+23:59), offset!(-23:59));
    assert_eq!(-offset!(-23:59), offset!(+23:59));
    assert_eq!(-offset!(+23:59:59), offset!(-23:59:59));
    assert_eq!(-offset!(-23:59:59), offset!(+23:59:59));
}

#[test]
fn local_offset_at() {
    #[cfg(not(target_family = "unix"))]
    assert!(UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH).is_ok());

    #[cfg(target_family = "unix")]
    let _ = UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH);
}

#[test]
fn current_local_offset() {
    #[cfg(not(target_family = "unix"))]
    assert!(UtcOffset::current_local_offset().is_ok());

    #[cfg(target_family = "unix")]
    let _ = UtcOffset::current_local_offset();
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use {
    SyntaxKind, TextUnit,
    smol_str::SmolStr,
};

#[derive(Clone, Debug)]
pub(crate) enum GreenNode {
    Leaf(GreenLeaf),
    Branch(Arc<GreenBranch>),
}

impl GreenNode {
    pub(crate) fn new_leaf(kind: SyntaxKind, text: &str) -> GreenNode {
        GreenNode::Leaf(GreenLeaf::new(kind, text))
    }

    pub(crate) fn new_branch(kind: SyntaxKind, children: Box<[GreenNode]>) -> GreenNode {
        GreenNode::Branch(Arc::new(GreenBranch::new(kind, children)))
    }

    pub fn kind(&self) -> SyntaxKind {
        match self {
            GreenNode::Leaf(l) => l.kind(),
            GreenNode::Branch(b) => b.kind(),
        }
    }

    pub fn text_len(&self) -> TextUnit {
        match self {
            GreenNode::Leaf(l) => l.text_len(),
            GreenNode::Branch(b) => b.text_len(),
        }
    }

    pub fn children(&self) -> &[GreenNode] {
        match self {
            GreenNode::Leaf(_) => &[],
            GreenNode::Branch(b) => b.children(),
        }
    }

    pub fn text(&self) -> String {
        let mut buff = String::new();
        go(self, &mut buff);
        return buff;
        fn go(node: &GreenNode, buff: &mut String) {
            match node {
                GreenNode::Leaf(l) => buff.push_str(&l.text()),
                GreenNode::Branch(b) => b.children().iter().for_each(|child| go(child, buff)),
            }
        }
    }
}

#[test]
fn assert_send_sync() {
    fn f<T: Send + Sync>() {}
    f::<GreenNode>();
}

#[derive(Clone, Debug)]
pub(crate) struct GreenBranch {
    text_len: TextUnit,
    kind: SyntaxKind,
    children: Box<[GreenNode]>,
}

impl GreenBranch {
    fn new(kind: SyntaxKind, children: Box<[GreenNode]>) -> GreenBranch {
        let text_len = children.iter().map(|x| x.text_len()).sum::<TextUnit>();
        GreenBranch {
            text_len,
            kind,
            children,
        }
    }

    pub fn kind(&self) -> SyntaxKind {
        self.kind
    }

    pub fn text_len(&self) -> TextUnit {
        self.text_len
    }

    pub fn children(&self) -> &[GreenNode] {
        &*self.children
    }
}

#[derive(Clone, Debug)]
pub(crate) struct GreenLeaf {
    kind: SyntaxKind,
    text: SmolStr,
}

impl GreenLeaf {
    fn new(kind: SyntaxKind, text: &str) -> Self {
        let text = SmolStr::new(text);
        GreenLeaf { kind, text }
    }

    pub(crate) fn kind(&self) -> SyntaxKind {
        self.kind
    }

    pub(crate) fn text(&self) -> &str {
        self.text.as_str()
    }

    pub(crate) fn text_len(&self) -> TextUnit {
        TextUnit::of_str(self.text())
    }
}

#[test]
fn test_sizes() {
    use std::mem::size_of;

    println!("GreenNode = {}", size_of::<GreenNode>());
    println!("GreenLeaf = {}", size_of::<GreenLeaf>());
    println!("SyntaxKind = {}", size_of::<SyntaxKind>());
    println!("SmolStr = {}", size_of::<SmolStr>());
}
<commit_msg>More premature layout micro optimizations<commit_after>use std::sync::Arc;
use {
    SyntaxKind, TextUnit,
    smol_str::SmolStr,
};

#[derive(Clone, Debug)]
pub(crate) enum GreenNode {
    Leaf {
        kind: SyntaxKind,
        text: SmolStr,
    },
    Branch(Arc<GreenBranch>),
}

impl GreenNode {
    pub(crate) fn new_leaf(kind: SyntaxKind, text: &str) -> GreenNode {
        GreenNode::Leaf { kind, text: SmolStr::new(text) }
    }

    pub(crate) fn new_branch(kind: SyntaxKind, children: Box<[GreenNode]>) -> GreenNode {
        GreenNode::Branch(Arc::new(GreenBranch::new(kind, children)))
    }

    pub fn kind(&self) -> SyntaxKind {
        match self {
            GreenNode::Leaf { kind, .. } => *kind,
            GreenNode::Branch(b) => b.kind(),
        }
    }

    pub fn text_len(&self) -> TextUnit {
        match self {
            GreenNode::Leaf { text, ..} => TextUnit::of_str(text.as_str()),
            GreenNode::Branch(b) => b.text_len(),
        }
    }

    pub fn children(&self) -> &[GreenNode] {
        match self {
            GreenNode::Leaf { .. } => &[],
            GreenNode::Branch(b) => b.children(),
        }
    }

    pub fn text(&self) -> String {
        let mut buff = String::new();
        go(self, &mut buff);
        return buff;
        fn go(node: &GreenNode, buff: &mut String) {
            match node {
                GreenNode::Leaf { text, .. } => buff.push_str(text.as_str()),
                GreenNode::Branch(b) => b.children().iter().for_each(|child| go(child, buff)),
            }
        }
    }
}

#[test]
fn assert_send_sync() {
    fn f<T: Send + Sync>() {}
    f::<GreenNode>();
}

#[derive(Clone, Debug)]
pub(crate) struct GreenBranch {
    text_len: TextUnit,
    kind: SyntaxKind,
    children: Box<[GreenNode]>,
}

impl GreenBranch {
    fn new(kind: SyntaxKind, children: Box<[GreenNode]>) -> GreenBranch {
        let text_len = children.iter().map(|x| x.text_len()).sum::<TextUnit>();
        GreenBranch {
            text_len,
            kind,
            children,
        }
    }

    pub fn kind(&self) -> SyntaxKind {
        self.kind
    }

    pub fn text_len(&self) -> TextUnit {
        self.text_len
    }

    pub fn children(&self) -> &[GreenNode] {
        &*self.children
    }
}

#[test]
fn test_sizes() {
    use std::mem::size_of;
    println!("GreenBranch = {}", size_of::<GreenBranch>());
    println!("GreenNode   = {}", size_of::<GreenNode>());
    println!("SmolStr     = {}", size_of::<SmolStr>());
}
<|endoftext|>


<commit_before><commit_msg>word_sub: allow arbitrary-length string changes<commit_after><|endoftext|>


<commit_before>/**
 * A deque, for fun.  Untested as of yet.  Likely buggy.
 */

type t[T] = obj {
            fn size() -> uint;

            fn add_front(&T t);
            fn add_back(&T t);

            fn pop_front() -> T;
            fn pop_back() -> T;

            fn peek_front() -> T;
            fn peek_back() -> T;

            fn get(int i) -> T;
};

fn create[T]() -> t[T] {

    type cell[T] = option::t[T];

    let uint initial_capacity = 32u; // 2^5

    /**
     * Grow is only called on full elts, so nelts is also len(elts), unlike
     * elsewhere.
     */
    fn grow[T](uint nelts, uint lo, vec[cell[T]] elts) -> vec[cell[T]] {
        assert (nelts == vec::len[cell[T]](elts));

        fn fill[T](uint i, uint nelts, uint lo,
                   vec[cell[T]] old) -> cell[T] {
            if (i < nelts) {
                ret old.((lo + i) % nelts);
            } else {
                ret option::none[T];
            }
        }

        let uint nalloc = uint::next_power_of_two(nelts + 1u);
        let vec::init_op[cell[T]] copy_op = bind fill[T](_, nelts, lo, elts);
        ret vec::init_fn[cell[T]](copy_op, nalloc);
    }

    fn get[T](vec[cell[T]] elts, uint i) -> T {
        alt (elts.(i)) {
            case (option::some[T](?t)) { ret t; }
            case (_) { fail; }
        }
        fail;   // FIXME: remove me when exhaustiveness checking works
    }

    obj deque[T](mutable uint nelts,
                 mutable uint lo,
                 mutable uint hi,
                 mutable vec[cell[T]] elts)
        {
            fn size() -> uint { ret nelts; }

            fn add_front(&T t) {
                let uint oldlo = lo;

                if (lo == 0u) {
                    lo = vec::len[cell[T]](elts) - 1u;
                } else {
                    lo -= 1u;
                }

                if (lo == hi) {
                    elts = grow[T](nelts, oldlo, elts);
                    lo = vec::len[cell[T]](elts) - 1u;
                    hi = nelts;
                }

                elts.(lo) = option::some[T](t);
                nelts += 1u;
            }

            fn add_back(&T t) {
                if (lo == hi && nelts != 0u) {
                    elts = grow[T](nelts, lo, elts);
                    lo = 0u;
                    hi = nelts;
                }

                elts.(hi) = option::some[T](t);
                hi = (hi + 1u) % vec::len[cell[T]](elts);
                nelts += 1u;
            }

            /**
             * We actually release (turn to none()) the T we're popping so
             * that we don't keep anyone's refcount up unexpectedly.
             */
            fn pop_front() -> T {
                let T t = get[T](elts, lo);
                elts.(lo) = option::none[T];
                lo = (lo + 1u) % vec::len[cell[T]](elts);
                nelts -= 1u;
                ret t;
            }

            fn pop_back() -> T {
                if (hi == 0u) {
                    hi = vec::len[cell[T]](elts) - 1u;
                } else {
                    hi -= 1u;
                }

                let T t = get[T](elts, hi);
                elts.(hi) = option::none[T];
                nelts -= 1u;
                ret t;
            }

            fn peek_front() -> T {
                ret get[T](elts, lo);
            }

            fn peek_back() -> T {
                ret get[T](elts, hi - 1u);
            }

            fn get(int i) -> T {
                let uint idx = (lo + (i as uint)) % vec::len[cell[T]](elts);
                ret get[T](elts, idx);
            }

        }
    let vec[cell[T]] v = vec::init_elt[cell[T]](option::none[T],
                                                initial_capacity);

    ret deque[T](0u, 0u, 0u, v);
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>stdlib: Use if/alt expressions in std::deque<commit_after>/**
 * A deque, for fun.  Untested as of yet.  Likely buggy.
 */

type t[T] = obj {
            fn size() -> uint;

            fn add_front(&T t);
            fn add_back(&T t);

            fn pop_front() -> T;
            fn pop_back() -> T;

            fn peek_front() -> T;
            fn peek_back() -> T;

            fn get(int i) -> T;
};

fn create[T]() -> t[T] {

    type cell[T] = option::t[T];

    let uint initial_capacity = 32u; // 2^5

    /**
     * Grow is only called on full elts, so nelts is also len(elts), unlike
     * elsewhere.
     */
    fn grow[T](uint nelts, uint lo, vec[cell[T]] elts) -> vec[cell[T]] {
        assert (nelts == vec::len[cell[T]](elts));

        fn fill[T](uint i, uint nelts, uint lo,
                   vec[cell[T]] old) -> cell[T] {
            ret if (i < nelts) {
                old.((lo + i) % nelts)
            } else {
                option::none[T]
            };
        }

        let uint nalloc = uint::next_power_of_two(nelts + 1u);
        let vec::init_op[cell[T]] copy_op = bind fill[T](_, nelts, lo, elts);
        ret vec::init_fn[cell[T]](copy_op, nalloc);
    }

    fn get[T](vec[cell[T]] elts, uint i) -> T {
        ret alt (elts.(i)) {
            case (option::some[T](?t)) { t }
            case (_) { fail }
        };
    }

    obj deque[T](mutable uint nelts,
                 mutable uint lo,
                 mutable uint hi,
                 mutable vec[cell[T]] elts)
        {
            fn size() -> uint { ret nelts; }

            fn add_front(&T t) {
                let uint oldlo = lo;

                if (lo == 0u) {
                    lo = vec::len[cell[T]](elts) - 1u;
                } else {
                    lo -= 1u;
                }

                if (lo == hi) {
                    elts = grow[T](nelts, oldlo, elts);
                    lo = vec::len[cell[T]](elts) - 1u;
                    hi = nelts;
                }

                elts.(lo) = option::some[T](t);
                nelts += 1u;
            }

            fn add_back(&T t) {
                if (lo == hi && nelts != 0u) {
                    elts = grow[T](nelts, lo, elts);
                    lo = 0u;
                    hi = nelts;
                }

                elts.(hi) = option::some[T](t);
                hi = (hi + 1u) % vec::len[cell[T]](elts);
                nelts += 1u;
            }

            /**
             * We actually release (turn to none()) the T we're popping so
             * that we don't keep anyone's refcount up unexpectedly.
             */
            fn pop_front() -> T {
                let T t = get[T](elts, lo);
                elts.(lo) = option::none[T];
                lo = (lo + 1u) % vec::len[cell[T]](elts);
                nelts -= 1u;
                ret t;
            }

            fn pop_back() -> T {
                if (hi == 0u) {
                    hi = vec::len[cell[T]](elts) - 1u;
                } else {
                    hi -= 1u;
                }

                let T t = get[T](elts, hi);
                elts.(hi) = option::none[T];
                nelts -= 1u;
                ret t;
            }

            fn peek_front() -> T {
                ret get[T](elts, lo);
            }

            fn peek_back() -> T {
                ret get[T](elts, hi - 1u);
            }

            fn get(int i) -> T {
                let uint idx = (lo + (i as uint)) % vec::len[cell[T]](elts);
                ret get[T](elts, idx);
            }

        }
    let vec[cell[T]] v = vec::init_elt[cell[T]](option::none[T],
                                                initial_capacity);

    ret deque[T](0u, 0u, 0u, v);
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, Ty, TyCtxt, adjustment::{PointerCast}};
use rustc_target::spec::abi;
use std::borrow::Cow;
use syntax_pos::Span;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    def_id: DefId,
    mir: &'a Mir<'tcx>,
) -> McfResult {
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in &predicates.predicates {
            match predicate {
                | Predicate::RegionOutlives(_)
                | Predicate::TypeOutlives(_)
                | Predicate::WellFormed(_)
                | Predicate::Projection(_)
                | Predicate::ConstEvaluatable(..) => continue,
                | Predicate::ObjectSafe(_) => {
                    bug!("object safe predicate on function: {:#?}", predicate)
                }
                Predicate::ClosureKind(..) => {
                    bug!("closure kind predicate on function: {:#?}", predicate)
                }
                Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().sty {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        }
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                }
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &mir.local_decls {
        check_ty(tcx, local.ty, local.source_info.span, def_id)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        mir.local_decls.iter().next().unwrap().source_info.span,
        def_id,
    )?;

    for bb in mir.basic_blocks() {
        check_terminator(tcx, mir, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, mir, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    ty: Ty<'tcx>,
    span: Span,
    fn_def_id: DefId,
) -> McfResult {
    for ty in ty.walk() {
        match ty.sty {
            ty::Ref(_, _, hir::Mutability::MutMutable) => return Err((
                span,
                "mutable references in const fn are unstable".into(),
            )),
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) {
                    return Err((span, "function pointers in const fn are unstable".into()))
                }
            }
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        | ty::ExistentialPredicate::AutoTrait(_)
                        | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ))
                        }
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
    Ok(())
}

fn check_rvalue(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, mir, operand, span)
        }
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
            check_place(tcx, mir, place, span)
        }
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(mir, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => Err((
                    span,
                    "casting pointers to ints is unstable in const fn".into(),
                )),
                (CastTy::RPtr(_), CastTy::Float) => bug!(),
                (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
                _ => check_operand(tcx, mir, operand, span),
            }
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _) => {
            check_operand(tcx, mir, operand, span)
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) |
        Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) |
        Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), _, _) => Err((
            span,
            "function pointer casts are not allowed in const fn".into(),
        )),
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => Err((
            span,
            "unsizing casts are not allowed in const fn".into(),
        )),
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, mir, lhs, span)?;
            check_operand(tcx, mir, rhs, span)?;
            let ty = lhs.ty(mir, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((
            span,
            "heap allocations are not allowed in const fn".into(),
        )),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(mir, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, mir, operand, span)
            } else {
                Err((
                    span,
                    "only int and `bool` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, mir, operand, span)?;
            }
            Ok(())
        }
    }
}

fn check_statement(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    statement: &Statement<'tcx>,
) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(place, rval) => {
            check_place(tcx, mir, place, span)?;
            check_rvalue(tcx, mir, rval, span)
        }

        StatementKind::FakeRead(_, place) => check_place(tcx, mir, place, span),

        // just an assignment
        StatementKind::SetDiscriminant { .. } => Ok(()),

        | StatementKind::InlineAsm { .. } => {
            Err((span, "cannot use inline assembly in const fn".into()))
        }

        // These are all NOPs
        | StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => {
            check_place(tcx, mir, place, span)
        }
        Operand::Constant(_) => Ok(()),
    }
}

fn check_place(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    place: &Place<'tcx>,
    span: Span,
) -> McfResult {
    match place {
        Place::Base(PlaceBase::Local(_)) => Ok(()),
        // promoteds are always fine, they are essentially constants
        Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Promoted(_), .. })) => Ok(()),
        Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Static(_), .. })) =>
            Err((span, "cannot access `static` items in const fn".into())),
        Place::Projection(proj) => {
            match proj.elem {
                | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. }
                | ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
                    check_place(tcx, mir, &proj.base, span)
                }
                | ProjectionElem::Downcast(..) => {
                    Err((span, "`match` or `if let` in `const fn` is unstable".into()))
                }
            }
        }
    }
}

fn check_terminator(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    terminator: &Terminator<'tcx>,
) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume => Ok(()),

        TerminatorKind::Drop { location, .. } => {
            check_place(tcx, mir, location, span)
        }
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, mir, location, span)?;
            check_operand(tcx, mir, value, span)
        },

        TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err((
            span,
            "`if`, `match`, `&&` and `||` are not stable in const fn".into(),
        )),
        | TerminatorKind::Abort | TerminatorKind::Unreachable => {
            Err((span, "const fn with unreachable code is not stable".into()))
        }
        | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        }

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
        } => {
            let fn_ty = func.ty(mir, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.sty {

                // some intrinsics are waved through if called inside the
                // standard library. Users never need to call them directly
                match tcx.fn_sig(def_id).abi() {
                    abi::Abi::RustIntrinsic => if !is_intrinsic_whitelisted(tcx, def_id) {
                        return Err((
                            span,
                            "can only call a curated list of intrinsics in `min_const_fn`".into(),
                        ))
                    },
                    abi::Abi::Rust if tcx.is_min_const_fn(def_id) => {},
                    abi::Abi::Rust => return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    )),
                    abi => return Err((
                        span,
                        format!(
                            "cannot call functions with `{}` abi in `min_const_fn`",
                            abi,
                        ).into(),
                    )),
                }

                check_operand(tcx, mir, func, span)?;

                for arg in args {
                    check_operand(tcx, mir, arg, span)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        }

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, mir, cond, span),

        TerminatorKind::FalseUnwind { .. } => {
            Err((span, "loops are not allowed in const fn".into()))
        },
    }
}

/// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted
/// for being called from stable `const fn`s (`min_const_fn`).
///
/// Adding more intrinsics requires sign-off from @rust-lang/lang.
fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
    match &tcx.item_name(def_id).as_str()[..] {
        | "size_of"
        | "min_align_of"
        | "needs_drop"
        // Arithmetic:
        | "add_with_overflow" // ~> .overflowing_add
        | "sub_with_overflow" // ~> .overflowing_sub
        | "mul_with_overflow" // ~> .overflowing_mul
        | "overflowing_add" // ~> .wrapping_add
        | "overflowing_sub" // ~> .wrapping_sub
        | "overflowing_mul" // ~> .wrapping_mul
        | "saturating_add" // ~> .saturating_add
        | "saturating_sub" // ~> .saturating_sub
        | "unchecked_shl" // ~> .wrapping_shl
        | "unchecked_shr" // ~> .wrapping_shr
        | "rotate_left" // ~> .rotate_left
        | "rotate_right" // ~> .rotate_right
        | "ctpop" // ~> .count_ones
        | "ctlz" // ~> .leading_zeros
        | "cttz" // ~> .trailing_zeros
        | "bswap" // ~> .swap_bytes
        | "bitreverse" // ~> .reverse_bits
        => true,
        _ => false,
    }
}
<commit_msg>Remove unused tcx and mir params<commit_after>use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, Ty, TyCtxt, adjustment::{PointerCast}};
use rustc_target::spec::abi;
use std::borrow::Cow;
use syntax_pos::Span;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    def_id: DefId,
    mir: &'a Mir<'tcx>,
) -> McfResult {
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in &predicates.predicates {
            match predicate {
                | Predicate::RegionOutlives(_)
                | Predicate::TypeOutlives(_)
                | Predicate::WellFormed(_)
                | Predicate::Projection(_)
                | Predicate::ConstEvaluatable(..) => continue,
                | Predicate::ObjectSafe(_) => {
                    bug!("object safe predicate on function: {:#?}", predicate)
                }
                Predicate::ClosureKind(..) => {
                    bug!("closure kind predicate on function: {:#?}", predicate)
                }
                Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().sty {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        }
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                }
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &mir.local_decls {
        check_ty(tcx, local.ty, local.source_info.span, def_id)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        mir.local_decls.iter().next().unwrap().source_info.span,
        def_id,
    )?;

    for bb in mir.basic_blocks() {
        check_terminator(tcx, mir, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, mir, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    ty: Ty<'tcx>,
    span: Span,
    fn_def_id: DefId,
) -> McfResult {
    for ty in ty.walk() {
        match ty.sty {
            ty::Ref(_, _, hir::Mutability::MutMutable) => return Err((
                span,
                "mutable references in const fn are unstable".into(),
            )),
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) {
                    return Err((span, "function pointers in const fn are unstable".into()))
                }
            }
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        | ty::ExistentialPredicate::AutoTrait(_)
                        | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ))
                        }
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
    Ok(())
}

fn check_rvalue(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(operand, span)
        }
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
            check_place(place, span)
        }
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(mir, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => Err((
                    span,
                    "casting pointers to ints is unstable in const fn".into(),
                )),
                (CastTy::RPtr(_), CastTy::Float) => bug!(),
                (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
                _ => check_operand(operand, span),
            }
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _) => {
            check_operand(operand, span)
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) |
        Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) |
        Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), _, _) => Err((
            span,
            "function pointer casts are not allowed in const fn".into(),
        )),
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => Err((
            span,
            "unsizing casts are not allowed in const fn".into(),
        )),
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(lhs, span)?;
            check_operand(rhs, span)?;
            let ty = lhs.ty(mir, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((
            span,
            "heap allocations are not allowed in const fn".into(),
        )),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(mir, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(operand, span)
            } else {
                Err((
                    span,
                    "only int and `bool` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(operand, span)?;
            }
            Ok(())
        }
    }
}

fn check_statement(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    statement: &Statement<'tcx>,
) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(place, rval) => {
            check_place(place, span)?;
            check_rvalue(tcx, mir, rval, span)
        }

        StatementKind::FakeRead(_, place) => check_place(place, span),

        // just an assignment
        StatementKind::SetDiscriminant { .. } => Ok(()),

        | StatementKind::InlineAsm { .. } => {
            Err((span, "cannot use inline assembly in const fn".into()))
        }

        // These are all NOPs
        | StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(
    operand: &Operand<'tcx>,
    span: Span,
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => {
            check_place(place, span)
        }
        Operand::Constant(_) => Ok(()),
    }
}

fn check_place(
    place: &Place<'tcx>,
    span: Span,
) -> McfResult {
    match place {
        Place::Base(PlaceBase::Local(_)) => Ok(()),
        // promoteds are always fine, they are essentially constants
        Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Promoted(_), .. })) => Ok(()),
        Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Static(_), .. })) =>
            Err((span, "cannot access `static` items in const fn".into())),
        Place::Projection(proj) => {
            match proj.elem {
                | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. }
                | ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
                    check_place(&proj.base, span)
                }
                | ProjectionElem::Downcast(..) => {
                    Err((span, "`match` or `if let` in `const fn` is unstable".into()))
                }
            }
        }
    }
}

fn check_terminator(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    terminator: &Terminator<'tcx>,
) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume => Ok(()),

        TerminatorKind::Drop { location, .. } => {
            check_place(location, span)
        }
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(location, span)?;
            check_operand(value, span)
        },

        TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err((
            span,
            "`if`, `match`, `&&` and `||` are not stable in const fn".into(),
        )),
        | TerminatorKind::Abort | TerminatorKind::Unreachable => {
            Err((span, "const fn with unreachable code is not stable".into()))
        }
        | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        }

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
        } => {
            let fn_ty = func.ty(mir, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.sty {

                // some intrinsics are waved through if called inside the
                // standard library. Users never need to call them directly
                match tcx.fn_sig(def_id).abi() {
                    abi::Abi::RustIntrinsic => if !is_intrinsic_whitelisted(tcx, def_id) {
                        return Err((
                            span,
                            "can only call a curated list of intrinsics in `min_const_fn`".into(),
                        ))
                    },
                    abi::Abi::Rust if tcx.is_min_const_fn(def_id) => {},
                    abi::Abi::Rust => return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    )),
                    abi => return Err((
                        span,
                        format!(
                            "cannot call functions with `{}` abi in `min_const_fn`",
                            abi,
                        ).into(),
                    )),
                }

                check_operand(func, span)?;

                for arg in args {
                    check_operand(arg, span)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        }

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(cond, span),

        TerminatorKind::FalseUnwind { .. } => {
            Err((span, "loops are not allowed in const fn".into()))
        },
    }
}

/// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted
/// for being called from stable `const fn`s (`min_const_fn`).
///
/// Adding more intrinsics requires sign-off from @rust-lang/lang.
fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
    match &tcx.item_name(def_id).as_str()[..] {
        | "size_of"
        | "min_align_of"
        | "needs_drop"
        // Arithmetic:
        | "add_with_overflow" // ~> .overflowing_add
        | "sub_with_overflow" // ~> .overflowing_sub
        | "mul_with_overflow" // ~> .overflowing_mul
        | "overflowing_add" // ~> .wrapping_add
        | "overflowing_sub" // ~> .wrapping_sub
        | "overflowing_mul" // ~> .wrapping_mul
        | "saturating_add" // ~> .saturating_add
        | "saturating_sub" // ~> .saturating_sub
        | "unchecked_shl" // ~> .wrapping_shl
        | "unchecked_shr" // ~> .wrapping_shr
        | "rotate_left" // ~> .rotate_left
        | "rotate_right" // ~> .rotate_right
        | "ctpop" // ~> .count_ones
        | "ctlz" // ~> .leading_zeros
        | "cttz" // ~> .trailing_zeros
        | "bswap" // ~> .swap_bytes
        | "bitreverse" // ~> .reverse_bits
        => true,
        _ => false,
    }
}
<|endoftext|>