<commit_before>use crate::fov::{Fov, PolarPoint};
use crate::hex::Dir6;
use crate::CellVector;
use euclid::vec2;
use num::Integer;

pub type HexFov<T> = Fov<HexPolarPoint, T>;

/// Points on a hex circle expressed in polar coordinates.
#[derive(Copy, Clone, PartialEq)]
pub struct HexPolarPoint {
    pos: f32,
    radius: u32,
}

impl HexPolarPoint {
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> i32 { (self.pos + 0.5).floor() as i32 }

    fn end_index(self) -> i32 { (self.pos + 0.5).ceil() as i32 }
}

impl PolarPoint for HexPolarPoint {
    fn unit_circle_endpoints() -> (Self, Self) {
        (
            HexPolarPoint {
                pos: 0.0,
                radius: 1,
            },
            HexPolarPoint {
                pos: 6.0,
                radius: 1,
            },
        )
    }

    fn is_below(&self, other: &HexPolarPoint) -> bool { self.winding_index() < other.end_index() }

    fn to_v2(&self) -> CellVector {
        if self.radius == 0 {
            return vec2(0, 0);
        }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as i32 * 6)) / self.radius as i32;
        let offset = index.mod_floor(&(self.radius as i32));

        let rod = Dir6::from_int(sector).to_v2();
        let tangent = Dir6::from_int(sector + 2).to_v2();

        rod * (self.radius as i32) + tangent * offset
    }

    fn expand(&self) -> Self {
        HexPolarPoint {
            pos: self.pos * (self.radius + 1) as f32 / self.radius as f32,
            radius: self.radius + 1,
        }
    }

    fn advance(&mut self) { self.pos = (self.pos + 0.5).floor() + 0.5; }
}

/// Special operations for FOV iterators using hex geometry.
pub trait HexFovIter: Sized {
    type Value;

    /// Extend a FOV iteration to make acute corners of fake-isometric hex map rooms visible.
    ///
    /// When using the standard hex logic, the two fake-isometric wall cells before the corner cell
    /// would block sight completely and make the corner invisible. This looks visually wrong, and
    /// can be fixed by applying this filter. The function parameter is used to check if a cell
    /// contains a wall. If there are three wall points that make up an acute corner, walking both
    /// of the closer two will make the third one added to the iterator output.
    ///
    /// This assumes you're using `HexPolarPoint` as the FOV type, in particular that the
    /// point moves clockwise as the coordinate value `advance`s.
    fn add_fake_isometric_acute_corners<F>(
        self,
        is_wall: F,
    ) -> AddFakeIsometricCorners<Self::Value, F, Self>
    where
        F: Fn(CellVector, &Self::Value) -> bool,
    {
        AddFakeIsometricCorners {
            inner: self,
            is_wall,
            prev: None,
            extra: None,
        }
    }
}

impl<T, I: Iterator<Item = (CellVector, T)>> HexFovIter for I {
    type Value = T;
}

pub struct AddFakeIsometricCorners<T, F, I> {
    inner: I,
    is_wall: F,
    prev: Option<(CellVector, T)>,
    extra: Option<(CellVector, T)>,
}

impl<T, F, I> Iterator for AddFakeIsometricCorners<T, F, I>
where
    T: Clone,
    F: Fn(CellVector, &T) -> bool,
    I: Iterator<Item = (CellVector, T)>,
{
    type Item = (CellVector, T);

    fn next(&mut self) -> Option<(Self::Item)> {
        use std::mem;

        if self.extra.is_some() {
            let mut ret = None;
            mem::swap(&mut ret, &mut self.extra);
            return ret;
        }

        let next = self.inner.next();

        if let (&Some(ref prev), &Some(ref next)) = (&self.prev, &next) {
            let prev_p = prev.0;
            let next_p = next.0;

            // Assume the polar coordinate rotates clockwise.
            if let Some(corner_p) = if next_p - prev_p == vec2(1, 1) {
                // Northeast side
                Some(prev_p + vec2(1, 0))
            } else if next_p - prev_p == vec2(-1, -1) {
                // Southwest side
                Some(prev_p + vec2(-1, 0))
            } else {
                None
            } {
                // We don't have the FOV value for the corner point, so just reuse the one from
                // `next` and hope it works out okay.
                if (self.is_wall)(prev.0, &prev.1)
                    && (self.is_wall)(next.0, &next.1)
                    && (self.is_wall)(corner_p, &next.1)
                {
                    // When the wall corner is found, push it to the extra slot to be returned
                    // in between this and the real next cell.
                    self.extra = Some((corner_p, next.1.clone()));
                }
            }
        }

        self.prev = next.clone();

        next
    }
}

#[cfg(test)]
mod test {
    use super::CellVector;
    use super::{HexFov, HexFovIter};
    use crate::fov::FovValue;
    use crate::hex::HexGeom;
    use euclid::vec2;
    use std::collections::HashMap;
    use std::iter::FromIterator;

    #[derive(PartialEq, Eq, Clone)]
    struct Cell {
        range: i32,
    }

    impl FovValue for Cell {
        fn advance(&self, offset: CellVector) -> Option<Self> {
            if offset.hex_dist() < self.range {
                Some(self.clone())
            } else {
                None
            }
        }
    }

    #[test]
    fn trivial_fov() {
        // Just draw a small circle.
        let field: HashMap<CellVector, Cell> = HashMap::from_iter(HexFov::new(Cell { range: 2 }));
        assert!(field.contains_key(&vec2(1, 0)));
        assert!(!field.contains_key(&vec2(1, -1)));

        // Now test out the fake-isometric corners.
        let field: HashMap<CellVector, Cell> = HashMap::from_iter(
            HexFov::new(Cell { range: 2 }).add_fake_isometric_acute_corners(|_p, _t| true),
        );
        assert!(field.contains_key(&vec2(1, 0)));
        assert!(field.contains_key(&vec2(1, -1)));
    }
}
<commit_msg>Fix warning<commit_after>use crate::fov::{Fov, PolarPoint};
use crate::hex::Dir6;
use crate::CellVector;
use euclid::vec2;
use num::Integer;

pub type HexFov<T> = Fov<HexPolarPoint, T>;

/// Points on a hex circle expressed in polar coordinates.
#[derive(Copy, Clone, PartialEq)]
pub struct HexPolarPoint {
    pos: f32,
    radius: u32,
}

impl HexPolarPoint {
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> i32 { (self.pos + 0.5).floor() as i32 }

    fn end_index(self) -> i32 { (self.pos + 0.5).ceil() as i32 }
}

impl PolarPoint for HexPolarPoint {
    fn unit_circle_endpoints() -> (Self, Self) {
        (
            HexPolarPoint {
                pos: 0.0,
                radius: 1,
            },
            HexPolarPoint {
                pos: 6.0,
                radius: 1,
            },
        )
    }

    fn is_below(&self, other: &HexPolarPoint) -> bool { self.winding_index() < other.end_index() }

    fn to_v2(&self) -> CellVector {
        if self.radius == 0 {
            return vec2(0, 0);
        }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as i32 * 6)) / self.radius as i32;
        let offset = index.mod_floor(&(self.radius as i32));

        let rod = Dir6::from_int(sector).to_v2();
        let tangent = Dir6::from_int(sector + 2).to_v2();

        rod * (self.radius as i32) + tangent * offset
    }

    fn expand(&self) -> Self {
        HexPolarPoint {
            pos: self.pos * (self.radius + 1) as f32 / self.radius as f32,
            radius: self.radius + 1,
        }
    }

    fn advance(&mut self) { self.pos = (self.pos + 0.5).floor() + 0.5; }
}

/// Special operations for FOV iterators using hex geometry.
pub trait HexFovIter: Sized {
    type Value;

    /// Extend a FOV iteration to make acute corners of fake-isometric hex map rooms visible.
    ///
    /// When using the standard hex logic, the two fake-isometric wall cells before the corner cell
    /// would block sight completely and make the corner invisible. This looks visually wrong, and
    /// can be fixed by applying this filter. The function parameter is used to check if a cell
    /// contains a wall. If there are three wall points that make up an acute corner, walking both
    /// of the closer two will make the third one added to the iterator output.
    ///
    /// This assumes you're using `HexPolarPoint` as the FOV type, in particular that the
    /// point moves clockwise as the coordinate value `advance`s.
    fn add_fake_isometric_acute_corners<F>(
        self,
        is_wall: F,
    ) -> AddFakeIsometricCorners<Self::Value, F, Self>
    where
        F: Fn(CellVector, &Self::Value) -> bool,
    {
        AddFakeIsometricCorners {
            inner: self,
            is_wall,
            prev: None,
            extra: None,
        }
    }
}

impl<T, I: Iterator<Item = (CellVector, T)>> HexFovIter for I {
    type Value = T;
}

pub struct AddFakeIsometricCorners<T, F, I> {
    inner: I,
    is_wall: F,
    prev: Option<(CellVector, T)>,
    extra: Option<(CellVector, T)>,
}

impl<T, F, I> Iterator for AddFakeIsometricCorners<T, F, I>
where
    T: Clone,
    F: Fn(CellVector, &T) -> bool,
    I: Iterator<Item = (CellVector, T)>,
{
    type Item = (CellVector, T);

    fn next(&mut self) -> Option<Self::Item> {
        use std::mem;

        if self.extra.is_some() {
            let mut ret = None;
            mem::swap(&mut ret, &mut self.extra);
            return ret;
        }

        let next = self.inner.next();

        if let (&Some(ref prev), &Some(ref next)) = (&self.prev, &next) {
            let prev_p = prev.0;
            let next_p = next.0;

            // Assume the polar coordinate rotates clockwise.
            if let Some(corner_p) = if next_p - prev_p == vec2(1, 1) {
                // Northeast side
                Some(prev_p + vec2(1, 0))
            } else if next_p - prev_p == vec2(-1, -1) {
                // Southwest side
                Some(prev_p + vec2(-1, 0))
            } else {
                None
            } {
                // We don't have the FOV value for the corner point, so just reuse the one from
                // `next` and hope it works out okay.
                if (self.is_wall)(prev.0, &prev.1)
                    && (self.is_wall)(next.0, &next.1)
                    && (self.is_wall)(corner_p, &next.1)
                {
                    // When the wall corner is found, push it to the extra slot to be returned
                    // in between this and the real next cell.
                    self.extra = Some((corner_p, next.1.clone()));
                }
            }
        }

        self.prev = next.clone();

        next
    }
}

#[cfg(test)]
mod test {
    use super::CellVector;
    use super::{HexFov, HexFovIter};
    use crate::fov::FovValue;
    use crate::hex::HexGeom;
    use euclid::vec2;
    use std::collections::HashMap;
    use std::iter::FromIterator;

    #[derive(PartialEq, Eq, Clone)]
    struct Cell {
        range: i32,
    }

    impl FovValue for Cell {
        fn advance(&self, offset: CellVector) -> Option<Self> {
            if offset.hex_dist() < self.range {
                Some(self.clone())
            } else {
                None
            }
        }
    }

    #[test]
    fn trivial_fov() {
        // Just draw a small circle.
        let field: HashMap<CellVector, Cell> = HashMap::from_iter(HexFov::new(Cell { range: 2 }));
        assert!(field.contains_key(&vec2(1, 0)));
        assert!(!field.contains_key(&vec2(1, -1)));

        // Now test out the fake-isometric corners.
        let field: HashMap<CellVector, Cell> = HashMap::from_iter(
            HexFov::new(Cell { range: 2 }).add_fake_isometric_acute_corners(|_p, _t| true),
        );
        assert!(field.contains_key(&vec2(1, 0)));
        assert!(field.contains_key(&vec2(1, -1)));
    }
}
<|endoftext|>


<commit_before>use crate::eval::AssignmentTarget;
use crate::syntax::IdentifierIndex;
use crate::syntax::identifiers::{SEMICOLON, COMMA};
use crate::value::implement::*;
use AmbiguousSyntax::*;
use ExpressionErrorPosition::*;
use crate::value::RightOperand;

#[derive(Debug, Clone)]
pub enum AmbiguousSyntax<'a> {
    MissingExpression,
    PartialTuple(Vec<BergVal<'a>>),
    TrailingComma(Vec<BergVal<'a>>),
    TrailingSemicolon,
    RawIdentifier(IdentifierIndex),
    Target(AssignmentTarget<'a>),
}

impl<'a> BergValue<'a> for AmbiguousSyntax<'a> {
    fn into_val(self) -> BergResult<'a> {
        self.err()
    }
    fn next_val(self) -> BergResult<'a, Option<NextVal<'a>>> {
        match self {
            Target(v) => v.next_val(),
            RawIdentifier(v) => v.next_val(),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().next_val(),
        }
    }
    fn into_native<T: TryFromBergVal<'a>>(self) -> BergResult<'a, T> {
        match self {
            Target(v) => v.into_native(),
            RawIdentifier(v) => v.into_native(),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().into_native(),
        }
    }
    fn try_into_native<T: TryFromBergVal<'a>>(self) -> BergResult<'a, Option<T>> {
        match self {
            Target(v) => v.try_into_native(),
            RawIdentifier(v) => v.try_into_native(),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().try_into_native(),
        }
    }

    fn infix(self, operator: IdentifierIndex, right: RightOperand<'a, impl BergValue<'a>>) -> BergResult<'a> {
        match self {
            Target(v) => v.infix(operator, right),
            RawIdentifier(v) => v.infix(operator, right),
            TrailingSemicolon if operator == SEMICOLON => BergError::MissingOperand.operand_err(LeftRight),
            TrailingComma(_) if operator == COMMA => BergError::MissingOperand.operand_err(LeftRight),
            PartialTuple(mut vec) => match operator {
                COMMA => { vec.push(right.into_val()?); PartialTuple(vec).disambiguate() },
                _ => PartialTuple(vec).disambiguate().infix(operator, right),
            }
            MissingExpression | TrailingSemicolon | TrailingComma(_) => self.disambiguate().infix(operator, right),
        }
    }

    fn infix_assign(self, operator: IdentifierIndex, right: RightOperand<'a, impl BergValue<'a>>) -> BergResult<'a> {
        match self {
            Target(v) => v.infix_assign(operator, right),
            RawIdentifier(v) => v.infix_assign(operator, right),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().infix_assign(operator, right),
        }
    }

    fn prefix(self, operator: IdentifierIndex) -> BergResult<'a> {
        match self {
            Target(v) => v.prefix(operator),
            RawIdentifier(v) => v.prefix(operator),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().prefix(operator),
        }
    }

    fn postfix(self, operator: IdentifierIndex) -> BergResult<'a> {
        match self {
            Target(v) => v.postfix(operator),
            RawIdentifier(v) => v.prefix(operator),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().postfix(operator),
        }
    }

    fn subexpression_result(self, boundary: ExpressionBoundary) -> BergResult<'a> {
        use ExpressionBoundary::*;
        match self {
            Target(v) => v.subexpression_result(boundary),
            RawIdentifier(v) => v.subexpression_result(boundary),
            MissingExpression if boundary == Parentheses || boundary == CurlyBraces => BergVal::empty_tuple().ok(),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().subexpression_result(boundary),
        }
    }

    fn field(self, name: IdentifierIndex) -> BergResult<'a, BergResult<'a>> {
        match self {
            Target(v) => v.field(name),
            RawIdentifier(v) => v.field(name),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().field(name),
        }
    }

    fn set_field(&mut self, name: IdentifierIndex, value: BergResult<'a>) -> BergResult<'a, ()> {
        match self {
            Target(v) => v.set_field(name, value),
            RawIdentifier(v) => v.set_field(name, value),
            MissingExpression => BergError::MissingOperand.err(),
            PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => panic!("not yet implemented: can't set field {} on {:?} to {}", name, self, value.display()),
        }
    }
}

impl<'a> From<AmbiguousSyntax<'a>> for ControlVal<'a> {
    fn from(from: AmbiguousSyntax<'a>) -> Self {
        ControlVal::AmbiguousSyntax(from)
    }
}

impl<'a> AmbiguousSyntax<'a> {
    pub fn result<T>(self) -> BergResult<'a, T> {
        Err(ControlVal::AmbiguousSyntax(self))
    }
    pub fn disambiguate(self) -> BergResult<'a> {
        use AmbiguousSyntax::*;
        match self {
            MissingExpression => BergError::MissingOperand.err(),
            PartialTuple(vec) | TrailingComma(vec) => Tuple::from(vec).ok(),
            TrailingSemicolon => BergVal::empty_tuple().ok(),
            RawIdentifier(v) => v.into_val(),
            Target(v) => v.get(),
        }
    }
    pub fn disambiguate_operand(self, new_position: ExpressionErrorPosition) -> BergResult<'a> {
        match self.disambiguate() {
            Err(ControlVal::AmbiguousSyntax(_)) => unreachable!(),
            Err(error) => error.at_position(new_position),
            Ok(value) => Ok(value),
        }
    }
}
<commit_msg>Make empty files yield () again<commit_after>use crate::eval::AssignmentTarget;
use crate::syntax::IdentifierIndex;
use crate::syntax::identifiers::{SEMICOLON, COMMA};
use crate::value::implement::*;
use AmbiguousSyntax::*;
use ExpressionErrorPosition::*;
use crate::value::RightOperand;

#[derive(Debug, Clone)]
pub enum AmbiguousSyntax<'a> {
    MissingExpression,
    PartialTuple(Vec<BergVal<'a>>),
    TrailingComma(Vec<BergVal<'a>>),
    TrailingSemicolon,
    RawIdentifier(IdentifierIndex),
    Target(AssignmentTarget<'a>),
}

impl<'a> BergValue<'a> for AmbiguousSyntax<'a> {
    fn into_val(self) -> BergResult<'a> {
        self.err()
    }
    fn next_val(self) -> BergResult<'a, Option<NextVal<'a>>> {
        match self {
            Target(v) => v.next_val(),
            RawIdentifier(v) => v.next_val(),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().next_val(),
        }
    }
    fn into_native<T: TryFromBergVal<'a>>(self) -> BergResult<'a, T> {
        match self {
            Target(v) => v.into_native(),
            RawIdentifier(v) => v.into_native(),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().into_native(),
        }
    }
    fn try_into_native<T: TryFromBergVal<'a>>(self) -> BergResult<'a, Option<T>> {
        match self {
            Target(v) => v.try_into_native(),
            RawIdentifier(v) => v.try_into_native(),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().try_into_native(),
        }
    }

    fn infix(self, operator: IdentifierIndex, right: RightOperand<'a, impl BergValue<'a>>) -> BergResult<'a> {
        match self {
            Target(v) => v.infix(operator, right),
            RawIdentifier(v) => v.infix(operator, right),
            TrailingSemicolon if operator == SEMICOLON => BergError::MissingOperand.operand_err(LeftRight),
            TrailingComma(_) if operator == COMMA => BergError::MissingOperand.operand_err(LeftRight),
            PartialTuple(mut vec) => match operator {
                COMMA => { vec.push(right.into_val()?); PartialTuple(vec).disambiguate() },
                _ => PartialTuple(vec).disambiguate().infix(operator, right),
            }
            MissingExpression | TrailingSemicolon | TrailingComma(_) => self.disambiguate().infix(operator, right),
        }
    }

    fn infix_assign(self, operator: IdentifierIndex, right: RightOperand<'a, impl BergValue<'a>>) -> BergResult<'a> {
        match self {
            Target(v) => v.infix_assign(operator, right),
            RawIdentifier(v) => v.infix_assign(operator, right),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().infix_assign(operator, right),
        }
    }

    fn prefix(self, operator: IdentifierIndex) -> BergResult<'a> {
        match self {
            Target(v) => v.prefix(operator),
            RawIdentifier(v) => v.prefix(operator),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().prefix(operator),
        }
    }

    fn postfix(self, operator: IdentifierIndex) -> BergResult<'a> {
        match self {
            Target(v) => v.postfix(operator),
            RawIdentifier(v) => v.prefix(operator),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().postfix(operator),
        }
    }

    fn subexpression_result(self, boundary: ExpressionBoundary) -> BergResult<'a> {
        use ExpressionBoundary::*;
        match self {
            Target(v) => v.subexpression_result(boundary),
            RawIdentifier(v) => v.subexpression_result(boundary),
            MissingExpression if boundary == Parentheses || boundary.is_block() => BergVal::empty_tuple().ok(),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().subexpression_result(boundary),
        }
    }

    fn field(self, name: IdentifierIndex) -> BergResult<'a, BergResult<'a>> {
        match self {
            Target(v) => v.field(name),
            RawIdentifier(v) => v.field(name),
            MissingExpression | PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => self.disambiguate().field(name),
        }
    }

    fn set_field(&mut self, name: IdentifierIndex, value: BergResult<'a>) -> BergResult<'a, ()> {
        match self {
            Target(v) => v.set_field(name, value),
            RawIdentifier(v) => v.set_field(name, value),
            MissingExpression => BergError::MissingOperand.err(),
            PartialTuple(_) | TrailingComma(_) | TrailingSemicolon => panic!("not yet implemented: can't set field {} on {:?} to {}", name, self, value.display()),
        }
    }
}

impl<'a> From<AmbiguousSyntax<'a>> for ControlVal<'a> {
    fn from(from: AmbiguousSyntax<'a>) -> Self {
        ControlVal::AmbiguousSyntax(from)
    }
}

impl<'a> AmbiguousSyntax<'a> {
    pub fn result<T>(self) -> BergResult<'a, T> {
        Err(ControlVal::AmbiguousSyntax(self))
    }
    pub fn disambiguate(self) -> BergResult<'a> {
        use AmbiguousSyntax::*;
        match self {
            MissingExpression => BergError::MissingOperand.err(),
            PartialTuple(vec) | TrailingComma(vec) => Tuple::from(vec).ok(),
            TrailingSemicolon => BergVal::empty_tuple().ok(),
            RawIdentifier(v) => v.into_val(),
            Target(v) => v.get(),
        }
    }
    pub fn disambiguate_operand(self, new_position: ExpressionErrorPosition) -> BergResult<'a> {
        match self.disambiguate() {
            Err(ControlVal::AmbiguousSyntax(_)) => unreachable!(),
            Err(error) => error.at_position(new_position),
            Ok(value) => Ok(value),
        }
    }
}
<|endoftext|>


<commit_before>//! Concrete error types for all operations which may be invalid in a certain const context.

use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_session::config::nightly_options;
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};

use super::ConstCx;

/// Emits an error if `op` is not allowed in the given const context.
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
    debug!("illegal_op: op={:?}", op);

    if op.is_allowed_in_item(ccx) {
        return;
    }

    if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
        ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
        return;
    }

    op.emit_error(ccx, span);
}

/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
    /// Returns the `Symbol` corresponding to the feature gate that would enable this operation,
    /// or `None` if such a feature gate does not exist.
    fn feature_gate() -> Option<Symbol> {
        None
    }

    /// Returns `true` if this operation is allowed in the given item.
    ///
    /// This check should assume that we are not in a non-const `fn`, where all operations are
    /// legal.
    ///
    /// By default, it returns `true` if and only if this operation has a corresponding feature
    /// gate and that gate is enabled.
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        Self::feature_gate().map_or(false, |gate| ccx.tcx.features().enabled(gate))
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0019,
            "{} contains unimplemented expression type",
            ccx.const_kind()
        );
        if let Some(feat) = Self::feature_gate() {
            err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feat));
        }
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "A function call isn't allowed in the const's initialization expression \
                      because the expression's value must be known at compile-time.",
            );
            err.note(
                "Remember: you can't use a function call inside a const's initialization \
                      expression! However, you can use it anywhere else.",
            );
        }
        err.emit();
    }
}

/// A function call where the callee is a pointer.
#[derive(Debug)]
pub struct FnCallIndirect;
impl NonConstOp for FnCallIndirect {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err =
            ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn");
        err.emit();
    }
}

/// A function call where the callee is not marked as `const`.
#[derive(Debug)]
pub struct FnCallNonConst(pub DefId);
impl NonConstOp for FnCallNonConst {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0015,
            "calls in {}s are limited to constant functions, \
             tuple structs and tuple variants",
            ccx.const_kind(),
        );
        err.emit();
    }
}

/// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
///
/// Contains the name of the feature that would allow the use of this function.
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Symbol);
impl NonConstOp for FnCallUnstable {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let FnCallUnstable(def_id, feature) = *self;

        let mut err = ccx.tcx.sess.struct_span_err(
            span,
            &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
        );
        if nightly_options::is_nightly_build() {
            err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct HeapAllocation;
impl NonConstOp for HeapAllocation {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0010,
            "allocations are not allowed in {}s",
            ccx.const_kind()
        );
        err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "The value of statics and constants must be known at compile time, \
                 and they live for the entire lifetime of a program. Creating a boxed \
                 value allocates memory on the heap at runtime, and therefore cannot \
                 be done at compile time.",
            );
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct IfOrMatch;
impl NonConstOp for IfOrMatch {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_if_match)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        // This should be caught by the HIR const-checker.
        ccx.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
    }
}

#[derive(Debug)]
pub struct InlineAsm;
impl NonConstOp for InlineAsm {}

#[derive(Debug)]
pub struct LiveDrop;
impl NonConstOp for LiveDrop {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        struct_span_err!(
            ccx.tcx.sess,
            span,
            E0493,
            "destructors cannot be evaluated at compile-time"
        )
        .span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()))
        .emit();
    }
}

#[derive(Debug)]
pub struct Loop;
impl NonConstOp for Loop {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_loop)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        // This should be caught by the HIR const-checker.
        ccx.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
    }
}

#[derive(Debug)]
pub struct CellBorrow;
impl NonConstOp for CellBorrow {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        struct_span_err!(
            ccx.tcx.sess,
            span,
            E0492,
            "cannot borrow a constant which may contain \
            interior mutability, create a static instead"
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct MutBorrow;
impl NonConstOp for MutBorrow {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_mut_refs)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_mut_refs,
            span,
            &format!(
                "references in {}s may only refer \
                      to immutable values",
                ccx.const_kind()
            ),
        );
        err.span_label(span, format!("{}s require immutable values", ccx.const_kind()));
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "References in statics and constants may only refer \
                      to immutable values.\n\n\
                      Statics are shared everywhere, and if they refer to \
                      mutable data one might violate memory safety since \
                      holding multiple mutable references to shared data \
                      is not allowed.\n\n\
                      If you really want global mutable state, try using \
                      static mut or a global UnsafeCell.",
            );
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct MutAddressOf;
impl NonConstOp for MutAddressOf {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_mut_refs)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_mut_refs,
            span,
            &format!("`&raw mut` is not allowed in {}s", ccx.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct MutDeref;
impl NonConstOp for MutDeref {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_mut_refs)
    }
}

#[derive(Debug)]
pub struct Panic;
impl NonConstOp for Panic {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_panic)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_panic,
            span,
            &format!("panicking in {}s is unstable", ccx.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrComparison;
impl NonConstOp for RawPtrComparison {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_compare_raw_pointers)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_compare_raw_pointers,
            span,
            &format!("comparing raw pointers inside {}", ccx.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrDeref;
impl NonConstOp for RawPtrDeref {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_raw_ptr_deref)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_raw_ptr_deref,
            span,
            &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrToIntCast;
impl NonConstOp for RawPtrToIntCast {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_raw_ptr_to_usize_cast)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_raw_ptr_to_usize_cast,
            span,
            &format!("casting pointers to integers in {}s is unstable", ccx.const_kind(),),
        )
        .emit();
    }
}

/// An access to a (non-thread-local) `static`.
#[derive(Debug)]
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        matches!(ccx.const_kind(), hir::ConstContext::Static(_))
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0013,
            "{}s cannot refer to statics",
            ccx.const_kind()
        );
        err.help(
            "consider extracting the value of the `static` to a `const`, and referring to that",
        );
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "`static` and `const` variables can refer to other `const` variables. \
                    A `const` variable, however, cannot refer to a `static` variable.",
            );
            err.help("To fix this, the value can be extracted to a `const` and then used.");
        }
        err.emit();
    }
}

/// An access to a thread-local `static`.
#[derive(Debug)]
pub struct ThreadLocalAccess;
impl NonConstOp for ThreadLocalAccess {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        struct_span_err!(
            ccx.tcx.sess,
            span,
            E0625,
            "thread-local statics cannot be \
            accessed at compile-time"
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        // Union accesses are stable in all contexts except `const fn`.
        ccx.const_kind() != hir::ConstContext::ConstFn
            || ccx.tcx.features().enabled(Self::feature_gate().unwrap())
    }

    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_fn_union)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_fn_union,
            span,
            "unions in const fn are unstable",
        )
        .emit();
    }
}
<commit_msg>forbid mutable references in all constants except const fns<commit_after>//! Concrete error types for all operations which may be invalid in a certain const context.

use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_session::config::nightly_options;
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};

use super::ConstCx;

/// Emits an error if `op` is not allowed in the given const context.
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
    debug!("illegal_op: op={:?}", op);

    if op.is_allowed_in_item(ccx) {
        return;
    }

    if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
        ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
        return;
    }

    op.emit_error(ccx, span);
}

/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
    /// Returns the `Symbol` corresponding to the feature gate that would enable this operation,
    /// or `None` if such a feature gate does not exist.
    fn feature_gate() -> Option<Symbol> {
        None
    }

    /// Returns `true` if this operation is allowed in the given item.
    ///
    /// This check should assume that we are not in a non-const `fn`, where all operations are
    /// legal.
    ///
    /// By default, it returns `true` if and only if this operation has a corresponding feature
    /// gate and that gate is enabled.
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        Self::feature_gate().map_or(false, |gate| ccx.tcx.features().enabled(gate))
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0019,
            "{} contains unimplemented expression type",
            ccx.const_kind()
        );
        if let Some(feat) = Self::feature_gate() {
            err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feat));
        }
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "A function call isn't allowed in the const's initialization expression \
                      because the expression's value must be known at compile-time.",
            );
            err.note(
                "Remember: you can't use a function call inside a const's initialization \
                      expression! However, you can use it anywhere else.",
            );
        }
        err.emit();
    }
}

/// A function call where the callee is a pointer.
#[derive(Debug)]
pub struct FnCallIndirect;
impl NonConstOp for FnCallIndirect {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err =
            ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn");
        err.emit();
    }
}

/// A function call where the callee is not marked as `const`.
#[derive(Debug)]
pub struct FnCallNonConst(pub DefId);
impl NonConstOp for FnCallNonConst {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0015,
            "calls in {}s are limited to constant functions, \
             tuple structs and tuple variants",
            ccx.const_kind(),
        );
        err.emit();
    }
}

/// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
///
/// Contains the name of the feature that would allow the use of this function.
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Symbol);
impl NonConstOp for FnCallUnstable {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let FnCallUnstable(def_id, feature) = *self;

        let mut err = ccx.tcx.sess.struct_span_err(
            span,
            &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
        );
        if nightly_options::is_nightly_build() {
            err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct HeapAllocation;
impl NonConstOp for HeapAllocation {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0010,
            "allocations are not allowed in {}s",
            ccx.const_kind()
        );
        err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "The value of statics and constants must be known at compile time, \
                 and they live for the entire lifetime of a program. Creating a boxed \
                 value allocates memory on the heap at runtime, and therefore cannot \
                 be done at compile time.",
            );
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct IfOrMatch;
impl NonConstOp for IfOrMatch {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_if_match)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        // This should be caught by the HIR const-checker.
        ccx.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
    }
}

#[derive(Debug)]
pub struct InlineAsm;
impl NonConstOp for InlineAsm {}

#[derive(Debug)]
pub struct LiveDrop;
impl NonConstOp for LiveDrop {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        struct_span_err!(
            ccx.tcx.sess,
            span,
            E0493,
            "destructors cannot be evaluated at compile-time"
        )
        .span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()))
        .emit();
    }
}

#[derive(Debug)]
pub struct Loop;
impl NonConstOp for Loop {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_loop)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        // This should be caught by the HIR const-checker.
        ccx.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
    }
}

#[derive(Debug)]
pub struct CellBorrow;
impl NonConstOp for CellBorrow {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        struct_span_err!(
            ccx.tcx.sess,
            span,
            E0492,
            "cannot borrow a constant which may contain \
            interior mutability, create a static instead"
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct MutBorrow;
impl NonConstOp for MutBorrow {
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        // Forbid everywhere except in const fn
        ccx.const_kind() == hir::ConstContext::ConstFn
            && ccx.tcx.features().enabled(Self::feature_gate().unwrap())
    }

    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_mut_refs)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_mut_refs,
            span,
            &format!(
                "references in {}s may only refer \
                      to immutable values",
                ccx.const_kind()
            ),
        );
        err.span_label(span, format!("{}s require immutable values", ccx.const_kind()));
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "References in statics and constants may only refer \
                      to immutable values.\n\n\
                      Statics are shared everywhere, and if they refer to \
                      mutable data one might violate memory safety since \
                      holding multiple mutable references to shared data \
                      is not allowed.\n\n\
                      If you really want global mutable state, try using \
                      static mut or a global UnsafeCell.",
            );
        }
        err.emit();
    }
}

#[derive(Debug)]
pub struct MutAddressOf;
impl NonConstOp for MutAddressOf {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_mut_refs)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_mut_refs,
            span,
            &format!("`&raw mut` is not allowed in {}s", ccx.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct MutDeref;
impl NonConstOp for MutDeref {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_mut_refs)
    }
}

#[derive(Debug)]
pub struct Panic;
impl NonConstOp for Panic {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_panic)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_panic,
            span,
            &format!("panicking in {}s is unstable", ccx.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrComparison;
impl NonConstOp for RawPtrComparison {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_compare_raw_pointers)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_compare_raw_pointers,
            span,
            &format!("comparing raw pointers inside {}", ccx.const_kind()),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrDeref;
impl NonConstOp for RawPtrDeref {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_raw_ptr_deref)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_raw_ptr_deref,
            span,
            &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),),
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct RawPtrToIntCast;
impl NonConstOp for RawPtrToIntCast {
    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_raw_ptr_to_usize_cast)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_raw_ptr_to_usize_cast,
            span,
            &format!("casting pointers to integers in {}s is unstable", ccx.const_kind(),),
        )
        .emit();
    }
}

/// An access to a (non-thread-local) `static`.
#[derive(Debug)]
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        matches!(ccx.const_kind(), hir::ConstContext::Static(_))
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        let mut err = struct_span_err!(
            ccx.tcx.sess,
            span,
            E0013,
            "{}s cannot refer to statics",
            ccx.const_kind()
        );
        err.help(
            "consider extracting the value of the `static` to a `const`, and referring to that",
        );
        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
            err.note(
                "`static` and `const` variables can refer to other `const` variables. \
                    A `const` variable, however, cannot refer to a `static` variable.",
            );
            err.help("To fix this, the value can be extracted to a `const` and then used.");
        }
        err.emit();
    }
}

/// An access to a thread-local `static`.
#[derive(Debug)]
pub struct ThreadLocalAccess;
impl NonConstOp for ThreadLocalAccess {
    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        struct_span_err!(
            ccx.tcx.sess,
            span,
            E0625,
            "thread-local statics cannot be \
            accessed at compile-time"
        )
        .emit();
    }
}

#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
    fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
        // Union accesses are stable in all contexts except `const fn`.
        ccx.const_kind() != hir::ConstContext::ConstFn
            || ccx.tcx.features().enabled(Self::feature_gate().unwrap())
    }

    fn feature_gate() -> Option<Symbol> {
        Some(sym::const_fn_union)
    }

    fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
        feature_err(
            &ccx.tcx.sess.parse_sess,
            sym::const_fn_union,
            span,
            "unions in const fn are unstable",
        )
        .emit();
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Transaction pool implementation leveraging txhashset for chain state
//! validation. It is a valid operation to add a tx to the tx pool if the
//! resulting tx pool can be added to the current chain state to produce a
//! valid chain state.

use self::core::core::hash::{Hash, Hashed};
use self::core::core::id::ShortId;
use self::core::core::verifier_cache::VerifierCache;
use self::core::core::{transaction, Block, BlockHeader, Transaction, Weighting};
use self::util::RwLock;
use crate::pool::Pool;
use crate::types::{BlockChain, PoolAdapter, PoolConfig, PoolEntry, PoolError, TxSource};
use chrono::prelude::*;
use grin_core as core;
use grin_util as util;
use std::collections::VecDeque;
use std::sync::Arc;

/// Transaction pool implementation.
pub struct TransactionPool {
	/// Pool Config
	pub config: PoolConfig,
	/// Our transaction pool.
	pub txpool: Pool,
	/// Our Dandelion "stempool".
	pub stempool: Pool,
	/// Cache of previous txs in case of a re-org.
	pub reorg_cache: Arc<RwLock<VecDeque<PoolEntry>>>,
	/// The blockchain
	pub blockchain: Arc<dyn BlockChain>,
	pub verifier_cache: Arc<RwLock<dyn VerifierCache>>,
	/// The pool adapter
	pub adapter: Arc<dyn PoolAdapter>,
}

impl TransactionPool {
	/// Create a new transaction pool
	pub fn new(
		config: PoolConfig,
		chain: Arc<dyn BlockChain>,
		verifier_cache: Arc<RwLock<dyn VerifierCache>>,
		adapter: Arc<dyn PoolAdapter>,
	) -> TransactionPool {
		TransactionPool {
			config,
			txpool: Pool::new(chain.clone(), verifier_cache.clone(), "txpool".to_string()),
			stempool: Pool::new(
				chain.clone(),
				verifier_cache.clone(),
				"stempool".to_string(),
			),
			reorg_cache: Arc::new(RwLock::new(VecDeque::new())),
			blockchain: chain,
			verifier_cache,
			adapter,
		}
	}

	pub fn chain_head(&self) -> Result<BlockHeader, PoolError> {
		self.blockchain.chain_head()
	}

	// Add tx to stempool (passing in all txs from txpool to validate against).
	fn add_to_stempool(&mut self, entry: PoolEntry, header: &BlockHeader) -> Result<(), PoolError> {
		self.stempool
			.add_to_pool(entry, self.txpool.all_transactions(), header)?;
		Ok(())
	}

	fn add_to_reorg_cache(&mut self, entry: PoolEntry) {
		let mut cache = self.reorg_cache.write();
		cache.push_back(entry);

		// We cache 30 mins of txs but we have a hard limit to avoid catastrophic failure.
		// For simplicity use the same value as the actual tx pool limit.
		if cache.len() > self.config.max_pool_size {
			let _ = cache.pop_front();
		}
		debug!("added tx to reorg_cache: size now {}", cache.len());
	}

	fn add_to_txpool(
		&mut self,
		mut entry: PoolEntry,
		header: &BlockHeader,
	) -> Result<(), PoolError> {
		// First deaggregate the tx based on current txpool txs.
		if entry.tx.kernels().len() > 1 {
			let txs = self.txpool.find_matching_transactions(entry.tx.kernels());
			if !txs.is_empty() {
				let tx = transaction::deaggregate(entry.tx, txs)?;

				// Validate this deaggregated tx "as tx", subject to regular tx weight limits.
				tx.validate(Weighting::AsTransaction, self.verifier_cache.clone())?;

				entry.tx = tx;
				entry.src.debug_name = "deagg".to_string();
			}
		}
		self.txpool.add_to_pool(entry.clone(), vec![], header)?;

		// We now need to reconcile the stempool based on the new state of the txpool.
		// Some stempool txs may no longer be valid and we need to evict them.
		{
			let txpool_tx = self.txpool.all_transactions_aggregate()?;
			self.stempool.reconcile(txpool_tx, header)?;
		}
		Ok(())
	}

	/// Add the given tx to the pool, directing it to either the stempool or
	/// txpool based on stem flag provided.
	pub fn add_to_pool(
		&mut self,
		src: TxSource,
		tx: Transaction,
		stem: bool,
		header: &BlockHeader,
	) -> Result<(), PoolError> {
		// Quick check to deal with common case of seeing the *same* tx
		// broadcast from multiple peers simultaneously.
		if !stem && self.txpool.contains_tx(tx.hash()) {
			return Err(PoolError::DuplicateTx);
		}

		// Do we have the capacity to accept this transaction?
		let acceptability = self.is_acceptable(&tx, stem);
		let mut evict = false;
		if !stem && acceptability.as_ref().err() == Some(&PoolError::OverCapacity) {
			evict = true;
		} else if acceptability.is_err() {
			return acceptability;
		}

		// Make sure the transaction is valid before anything else.
		// Validate tx accounting for max tx weight.
		tx.validate(Weighting::AsTransaction, self.verifier_cache.clone())
			.map_err(PoolError::InvalidTx)?;

		// Check the tx lock_time is valid based on current chain state.
		self.blockchain.verify_tx_lock_height(&tx)?;

		// Check coinbase maturity before we go any further.
		self.blockchain.verify_coinbase_maturity(&tx)?;

		let entry = PoolEntry {
			src,
			tx_at: Utc::now(),
			tx,
		};

		// If not stem then we are fluff.
		// If this is a stem tx then attempt to stem.
		// Any problems during stem, fallback to fluff.
		if !stem
			|| self
				.add_to_stempool(entry.clone(), header)
				.and_then(|_| self.adapter.stem_tx_accepted(&entry.tx))
				.is_err()
		{
			self.add_to_txpool(entry.clone(), header)?;
			self.add_to_reorg_cache(entry.clone());
			self.adapter.tx_accepted(&entry.tx);
		}

		// Transaction passed all the checks but we have to make space for it
		if evict {
			self.evict_from_txpool();
		}

		Ok(())
	}

	// Remove the last transaction from the flattened bucket transactions.
	// No other tx depends on it, it has low fee_to_weight and is unlikely to participate in any cut-through.
	pub fn evict_from_txpool(&mut self) {
		// Get bucket transactions
		let bucket_transactions = self.txpool.bucket_transactions(Weighting::NoLimit);

		// Get last transaction and remove it
		match bucket_transactions.last() {
			Some(evictable_transaction) => {
				// Remove transaction
				self.txpool.entries = self
					.txpool
					.entries
					.iter()
					.filter(|x| x.tx != *evictable_transaction)
					.map(|x| x.clone())
					.collect::<Vec<_>>();
			}
			None => (),
		}
	}

	// Old txs will "age out" after 30 mins.
	pub fn truncate_reorg_cache(&mut self, cutoff: DateTime<Utc>) {
		let mut cache = self.reorg_cache.write();

		while cache.front().map(|x| x.tx_at < cutoff).unwrap_or(false) {
			let _ = cache.pop_front();
		}

		debug!("truncate_reorg_cache: size: {}", cache.len());
	}

	pub fn reconcile_reorg_cache(&mut self, header: &BlockHeader) -> Result<(), PoolError> {
		let entries = self.reorg_cache.read().iter().cloned().collect::<Vec<_>>();
		debug!(
			"reconcile_reorg_cache: size: {}, block: {:?} ...",
			entries.len(),
			header.hash(),
		);
		for entry in entries {
			let _ = &self.add_to_txpool(entry.clone(), header);
		}
		debug!(
			"reconcile_reorg_cache: block: {:?} ... done.",
			header.hash()
		);
		Ok(())
	}

	/// Reconcile the transaction pool (both txpool and stempool) against the
	/// provided block.
	pub fn reconcile_block(&mut self, block: &Block) -> Result<(), PoolError> {
		// First reconcile the txpool.
		self.txpool.reconcile_block(block);
		self.txpool.reconcile(None, &block.header)?;

		// Now reconcile our stempool, accounting for the updated txpool txs.
		self.stempool.reconcile_block(block);
		{
			let txpool_tx = self.txpool.all_transactions_aggregate()?;
			self.stempool.reconcile(txpool_tx, &block.header)?;
		}

		Ok(())
	}

	/// Retrieve individual transaction for the given kernel hash.
	pub fn retrieve_tx_by_kernel_hash(&self, hash: Hash) -> Option<Transaction> {
		self.txpool.retrieve_tx_by_kernel_hash(hash)
	}

	/// Retrieve all transactions matching the provided "compact block"
	/// based on the kernel set.
	/// Note: we only look in the txpool for this (stempool is under embargo).
	pub fn retrieve_transactions(
		&self,
		hash: Hash,
		nonce: u64,
		kern_ids: &[ShortId],
	) -> (Vec<Transaction>, Vec<ShortId>) {
		self.txpool.retrieve_transactions(hash, nonce, kern_ids)
	}

	/// Whether the transaction is acceptable to the pool, given both how
	/// full the pool is and the transaction weight.
	fn is_acceptable(&self, tx: &Transaction, stem: bool) -> Result<(), PoolError> {
		if self.total_size() > self.config.max_pool_size {
			// TODO evict old/large transactions instead
			return Err(PoolError::OverCapacity);
		}

		// Check that the stempool can accept this transaction
		if stem && self.stempool.size() > self.config.max_stempool_size {
			return Err(PoolError::OverCapacity);
		} else if self.total_size() > self.config.max_pool_size {
			return Err(PoolError::OverCapacity);
		}

		// for a basic transaction (1 input, 2 outputs) -
		// (-1 * 1) + (4 * 2) + 1 = 8
		// 8 * 10 = 80
		if self.config.accept_fee_base > 0 {
			let threshold = (tx.tx_weight() as u64) * self.config.accept_fee_base;
			if tx.fee() < threshold {
				return Err(PoolError::LowFeeTransaction(threshold));
			}
		}
		Ok(())
	}

	/// Get the total size of the pool.
	/// Note: we only consider the txpool here as stempool is under embargo.
	pub fn total_size(&self) -> usize {
		self.txpool.size()
	}

	/// Returns a vector of transactions from the txpool so we can build a
	/// block from them.
	pub fn prepare_mineable_transactions(&self) -> Result<Vec<Transaction>, PoolError> {
		self.txpool
			.prepare_mineable_transactions(self.config.mineable_max_weight)
	}
}
<commit_msg>Remove TODO (#2854)<commit_after>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Transaction pool implementation leveraging txhashset for chain state
//! validation. It is a valid operation to add a tx to the tx pool if the
//! resulting tx pool can be added to the current chain state to produce a
//! valid chain state.

use self::core::core::hash::{Hash, Hashed};
use self::core::core::id::ShortId;
use self::core::core::verifier_cache::VerifierCache;
use self::core::core::{transaction, Block, BlockHeader, Transaction, Weighting};
use self::util::RwLock;
use crate::pool::Pool;
use crate::types::{BlockChain, PoolAdapter, PoolConfig, PoolEntry, PoolError, TxSource};
use chrono::prelude::*;
use grin_core as core;
use grin_util as util;
use std::collections::VecDeque;
use std::sync::Arc;

/// Transaction pool implementation.
pub struct TransactionPool {
	/// Pool Config
	pub config: PoolConfig,
	/// Our transaction pool.
	pub txpool: Pool,
	/// Our Dandelion "stempool".
	pub stempool: Pool,
	/// Cache of previous txs in case of a re-org.
	pub reorg_cache: Arc<RwLock<VecDeque<PoolEntry>>>,
	/// The blockchain
	pub blockchain: Arc<dyn BlockChain>,
	pub verifier_cache: Arc<RwLock<dyn VerifierCache>>,
	/// The pool adapter
	pub adapter: Arc<dyn PoolAdapter>,
}

impl TransactionPool {
	/// Create a new transaction pool
	pub fn new(
		config: PoolConfig,
		chain: Arc<dyn BlockChain>,
		verifier_cache: Arc<RwLock<dyn VerifierCache>>,
		adapter: Arc<dyn PoolAdapter>,
	) -> TransactionPool {
		TransactionPool {
			config,
			txpool: Pool::new(chain.clone(), verifier_cache.clone(), "txpool".to_string()),
			stempool: Pool::new(
				chain.clone(),
				verifier_cache.clone(),
				"stempool".to_string(),
			),
			reorg_cache: Arc::new(RwLock::new(VecDeque::new())),
			blockchain: chain,
			verifier_cache,
			adapter,
		}
	}

	pub fn chain_head(&self) -> Result<BlockHeader, PoolError> {
		self.blockchain.chain_head()
	}

	// Add tx to stempool (passing in all txs from txpool to validate against).
	fn add_to_stempool(&mut self, entry: PoolEntry, header: &BlockHeader) -> Result<(), PoolError> {
		self.stempool
			.add_to_pool(entry, self.txpool.all_transactions(), header)?;
		Ok(())
	}

	fn add_to_reorg_cache(&mut self, entry: PoolEntry) {
		let mut cache = self.reorg_cache.write();
		cache.push_back(entry);

		// We cache 30 mins of txs but we have a hard limit to avoid catastrophic failure.
		// For simplicity use the same value as the actual tx pool limit.
		if cache.len() > self.config.max_pool_size {
			let _ = cache.pop_front();
		}
		debug!("added tx to reorg_cache: size now {}", cache.len());
	}

	fn add_to_txpool(
		&mut self,
		mut entry: PoolEntry,
		header: &BlockHeader,
	) -> Result<(), PoolError> {
		// First deaggregate the tx based on current txpool txs.
		if entry.tx.kernels().len() > 1 {
			let txs = self.txpool.find_matching_transactions(entry.tx.kernels());
			if !txs.is_empty() {
				let tx = transaction::deaggregate(entry.tx, txs)?;

				// Validate this deaggregated tx "as tx", subject to regular tx weight limits.
				tx.validate(Weighting::AsTransaction, self.verifier_cache.clone())?;

				entry.tx = tx;
				entry.src.debug_name = "deagg".to_string();
			}
		}
		self.txpool.add_to_pool(entry.clone(), vec![], header)?;

		// We now need to reconcile the stempool based on the new state of the txpool.
		// Some stempool txs may no longer be valid and we need to evict them.
		{
			let txpool_tx = self.txpool.all_transactions_aggregate()?;
			self.stempool.reconcile(txpool_tx, header)?;
		}
		Ok(())
	}

	/// Add the given tx to the pool, directing it to either the stempool or
	/// txpool based on stem flag provided.
	pub fn add_to_pool(
		&mut self,
		src: TxSource,
		tx: Transaction,
		stem: bool,
		header: &BlockHeader,
	) -> Result<(), PoolError> {
		// Quick check to deal with common case of seeing the *same* tx
		// broadcast from multiple peers simultaneously.
		if !stem && self.txpool.contains_tx(tx.hash()) {
			return Err(PoolError::DuplicateTx);
		}

		// Do we have the capacity to accept this transaction?
		let acceptability = self.is_acceptable(&tx, stem);
		let mut evict = false;
		if !stem && acceptability.as_ref().err() == Some(&PoolError::OverCapacity) {
			evict = true;
		} else if acceptability.is_err() {
			return acceptability;
		}

		// Make sure the transaction is valid before anything else.
		// Validate tx accounting for max tx weight.
		tx.validate(Weighting::AsTransaction, self.verifier_cache.clone())
			.map_err(PoolError::InvalidTx)?;

		// Check the tx lock_time is valid based on current chain state.
		self.blockchain.verify_tx_lock_height(&tx)?;

		// Check coinbase maturity before we go any further.
		self.blockchain.verify_coinbase_maturity(&tx)?;

		let entry = PoolEntry {
			src,
			tx_at: Utc::now(),
			tx,
		};

		// If not stem then we are fluff.
		// If this is a stem tx then attempt to stem.
		// Any problems during stem, fallback to fluff.
		if !stem
			|| self
				.add_to_stempool(entry.clone(), header)
				.and_then(|_| self.adapter.stem_tx_accepted(&entry.tx))
				.is_err()
		{
			self.add_to_txpool(entry.clone(), header)?;
			self.add_to_reorg_cache(entry.clone());
			self.adapter.tx_accepted(&entry.tx);
		}

		// Transaction passed all the checks but we have to make space for it
		if evict {
			self.evict_from_txpool();
		}

		Ok(())
	}

	// Remove the last transaction from the flattened bucket transactions.
	// No other tx depends on it, it has low fee_to_weight and is unlikely to participate in any cut-through.
	pub fn evict_from_txpool(&mut self) {
		// Get bucket transactions
		let bucket_transactions = self.txpool.bucket_transactions(Weighting::NoLimit);

		// Get last transaction and remove it
		match bucket_transactions.last() {
			Some(evictable_transaction) => {
				// Remove transaction
				self.txpool.entries = self
					.txpool
					.entries
					.iter()
					.filter(|x| x.tx != *evictable_transaction)
					.map(|x| x.clone())
					.collect::<Vec<_>>();
			}
			None => (),
		}
	}

	// Old txs will "age out" after 30 mins.
	pub fn truncate_reorg_cache(&mut self, cutoff: DateTime<Utc>) {
		let mut cache = self.reorg_cache.write();

		while cache.front().map(|x| x.tx_at < cutoff).unwrap_or(false) {
			let _ = cache.pop_front();
		}

		debug!("truncate_reorg_cache: size: {}", cache.len());
	}

	pub fn reconcile_reorg_cache(&mut self, header: &BlockHeader) -> Result<(), PoolError> {
		let entries = self.reorg_cache.read().iter().cloned().collect::<Vec<_>>();
		debug!(
			"reconcile_reorg_cache: size: {}, block: {:?} ...",
			entries.len(),
			header.hash(),
		);
		for entry in entries {
			let _ = &self.add_to_txpool(entry.clone(), header);
		}
		debug!(
			"reconcile_reorg_cache: block: {:?} ... done.",
			header.hash()
		);
		Ok(())
	}

	/// Reconcile the transaction pool (both txpool and stempool) against the
	/// provided block.
	pub fn reconcile_block(&mut self, block: &Block) -> Result<(), PoolError> {
		// First reconcile the txpool.
		self.txpool.reconcile_block(block);
		self.txpool.reconcile(None, &block.header)?;

		// Now reconcile our stempool, accounting for the updated txpool txs.
		self.stempool.reconcile_block(block);
		{
			let txpool_tx = self.txpool.all_transactions_aggregate()?;
			self.stempool.reconcile(txpool_tx, &block.header)?;
		}

		Ok(())
	}

	/// Retrieve individual transaction for the given kernel hash.
	pub fn retrieve_tx_by_kernel_hash(&self, hash: Hash) -> Option<Transaction> {
		self.txpool.retrieve_tx_by_kernel_hash(hash)
	}

	/// Retrieve all transactions matching the provided "compact block"
	/// based on the kernel set.
	/// Note: we only look in the txpool for this (stempool is under embargo).
	pub fn retrieve_transactions(
		&self,
		hash: Hash,
		nonce: u64,
		kern_ids: &[ShortId],
	) -> (Vec<Transaction>, Vec<ShortId>) {
		self.txpool.retrieve_transactions(hash, nonce, kern_ids)
	}

	/// Whether the transaction is acceptable to the pool, given both how
	/// full the pool is and the transaction weight.
	fn is_acceptable(&self, tx: &Transaction, stem: bool) -> Result<(), PoolError> {
		if self.total_size() > self.config.max_pool_size {
			return Err(PoolError::OverCapacity);
		}

		// Check that the stempool can accept this transaction
		if stem && self.stempool.size() > self.config.max_stempool_size {
			return Err(PoolError::OverCapacity);
		} else if self.total_size() > self.config.max_pool_size {
			return Err(PoolError::OverCapacity);
		}

		// for a basic transaction (1 input, 2 outputs) -
		// (-1 * 1) + (4 * 2) + 1 = 8
		// 8 * 10 = 80
		if self.config.accept_fee_base > 0 {
			let threshold = (tx.tx_weight() as u64) * self.config.accept_fee_base;
			if tx.fee() < threshold {
				return Err(PoolError::LowFeeTransaction(threshold));
			}
		}
		Ok(())
	}

	/// Get the total size of the pool.
	/// Note: we only consider the txpool here as stempool is under embargo.
	pub fn total_size(&self) -> usize {
		self.txpool.size()
	}

	/// Returns a vector of transactions from the txpool so we can build a
	/// block from them.
	pub fn prepare_mineable_transactions(&self) -> Result<Vec<Transaction>, PoolError> {
		self.txpool
			.prepare_mineable_transactions(self.config.mineable_max_weight)
	}
}
<|endoftext|>


<commit_before>extern crate cc;
extern crate gst_plugin_version_helper;
extern crate pkg_config;

fn main() {
    gst_plugin_version_helper::get_info();

    if cfg!(feature = "v1_18") {
        return;
    }

    let gstreamer = pkg_config::probe_library("gstreamer-1.0").unwrap();
    let includes = [gstreamer.include_paths];

    let files = ["src/base/gstaggregator.c"];

    let mut build = cc::Build::new();
    build.include("src/base");

    for f in files.iter() {
        build.file(f);
    }

    for p in includes.iter().flat_map(|i| i) {
        build.include(p);
    }

    build.define(
        "PACKAGE_BUGREPORT",
        "\"https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/new\"",
    );
    build.define("GstAggregator", "GstAggregatorFallback");
    build.define("GstAggregatorClass", "GstAggregatorFallbackClass");
    build.define("GstAggregatorPrivate", "GstAggregatorFallbackPrivate");
    build.define("GstAggregatorPad", "GstAggregatorFallbackPad");
    build.define("GstAggregatorPadClass", "GstAggregatorFallbackPadClass");
    build.define("GstAggregatorPadPrivate", "GstAggregatorFallbackPadPrivate");
    build.define("GST_BASE_API", "G_GNUC_INTERNAL");

    build.compile("libgstaggregator-c.a");
}
<commit_msg>fallbackswitch: disable extra warnings for aggregator build<commit_after>extern crate cc;
extern crate gst_plugin_version_helper;
extern crate pkg_config;

fn main() {
    gst_plugin_version_helper::get_info();

    if cfg!(feature = "v1_18") {
        return;
    }

    let gstreamer = pkg_config::probe_library("gstreamer-1.0").unwrap();
    let includes = [gstreamer.include_paths];

    let files = ["src/base/gstaggregator.c"];

    let mut build = cc::Build::new();
    build.include("src/base");

    for f in files.iter() {
        build.file(f);
    }

    for p in includes.iter().flat_map(|i| i) {
        build.include(p);
    }

    build.define(
        "PACKAGE_BUGREPORT",
        "\"https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/new\"",
    );
    build.extra_warnings(false);
    build.define("GstAggregator", "GstAggregatorFallback");
    build.define("GstAggregatorClass", "GstAggregatorFallbackClass");
    build.define("GstAggregatorPrivate", "GstAggregatorFallbackPrivate");
    build.define("GstAggregatorPad", "GstAggregatorFallbackPad");
    build.define("GstAggregatorPadClass", "GstAggregatorFallbackPadClass");
    build.define("GstAggregatorPadPrivate", "GstAggregatorFallbackPadPrivate");
    build.define("GST_BASE_API", "G_GNUC_INTERNAL");

    build.compile("libgstaggregator-c.a");
}
<|endoftext|>


<commit_before>//
// Copyright 2018 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

extern crate byteorder;
extern crate fmt;
#[macro_use]
extern crate log;
extern crate protobuf;

use byteorder::WriteBytesExt;
use proto::oak_api::OakStatus;
use protobuf::ProtobufEnum;
use std::io;
use std::io::Write;

pub mod grpc;
pub mod proto;
pub mod storage;
#[cfg(test)]
mod tests;
pub mod wasm;

#[cfg(test)]
#[macro_use]
extern crate assert_matches;

/// Handle used to identify read or write channel halves.
///
/// These handles are used for all host function calls.
pub type Handle = u64;

/// Map an [`OakStatus`] value to the nearest available [`std::io::Result`].
fn result_from_status<T>(status: Option<OakStatus>, val: T) -> std::io::Result<T> {
    match status {
        Some(OakStatus::OAK_STATUS_UNSPECIFIED) => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unspecified Oak status value",
        )),
        Some(OakStatus::OK) => Ok(val),
        Some(OakStatus::ERR_BAD_HANDLE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Bad handle"))
        }
        Some(OakStatus::ERR_INVALID_ARGS) => Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Invalid arguments",
        )),
        Some(OakStatus::ERR_CHANNEL_CLOSED) => Err(io::Error::new(
            io::ErrorKind::ConnectionReset,
            "Channel closed",
        )),
        Some(OakStatus::ERR_BUFFER_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Buffer too small",
        )),
        Some(OakStatus::ERR_HANDLE_SPACE_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Handle space too small",
        )),
        Some(OakStatus::ERR_OUT_OF_RANGE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Out of range"))
        }
        Some(OakStatus::ERR_INTERNAL) => {
            Err(io::Error::new(io::ErrorKind::Other, "Internal error"))
        }
        None => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unknown Oak status value",
        )),
    }
}

/// Create a new unidirectional channel.
///
/// On success, returns [`Handle`] values for the write and read halves
/// (respectively).
pub fn channel_create() -> Result<(Handle, Handle), OakStatus> {
    let mut write: Handle = 0;
    let mut read: Handle = 0;
    match OakStatus::from_i32(unsafe {
        wasm::channel_create(&mut write as *mut u64, &mut read as *mut u64) // @@@ check endianness
    }) {
        Some(OakStatus::OK) => Ok((write, read)),
        Some(err) => Err(err),
        None => Err(OakStatus::OAK_STATUS_UNSPECIFIED),
    }
}

/// Close the specified channel [`Handle`].
pub fn channel_close(handle: Handle) -> OakStatus {
    match OakStatus::from_i32(unsafe { wasm::channel_close(handle) }) {
        Some(s) => s,
        None => OakStatus::OAK_STATUS_UNSPECIFIED,
    }
}

/// Determine the [`Handle`] for a pre-defined channel, identified by its
/// `port_name`.
pub fn channel_find(port_name: &str) -> Handle {
    unsafe { wasm::channel_find(port_name.as_ptr(), port_name.len()) }
}

// Build a chunk of memory that is suitable for passing to wasm::wait_on_channels,
// holding the given collection of channel handles.
fn new_handle_space(handles: &[Handle]) -> Vec<u8> {
    let mut space = Vec::with_capacity(wasm::SPACE_BYTES_PER_HANDLE * handles.len());
    for handle in handles {
        space.write_u64::<byteorder::LittleEndian>(*handle).unwrap();
        space.push(0x00);
    }
    space
}

// Prepare a handle space for polling by clearing all of the message-pending
// indicator bytes.
fn prep_handle_space(space: &mut [u8]) {
    let count = space.len() / 8;
    for i in 0..count {
        space[i * wasm::SPACE_BYTES_PER_HANDLE + (wasm::SPACE_BYTES_PER_HANDLE - 1)] = 0;
    }
}

/// Wait for one or more of the provided handles to become ready for reading
/// from.
///
/// This is a convenience wrapper around the [`wasm::wait_on_channels`] host
/// function. This version is easier to use in Rust but is less efficient
/// (because the notification space is re-created on each invocation).
pub fn wait_on_channels(handles: &[Handle]) -> Result<Vec<Handle>, OakStatus> {
    let mut space = new_handle_space(handles);
    unsafe {
        let status = wasm::wait_on_channels(space.as_mut_ptr(), handles.len() as u32);
        match OakStatus::from_i32(status) {
            Some(OakStatus::OK) => (),
            Some(err) => return Err(err),
            None => return Err(OakStatus::OAK_STATUS_UNSPECIFIED),
        }
        let mut results = Vec::with_capacity(handles.len());
        for i in 0..handles.len() {
            if space[i * wasm::SPACE_BYTES_PER_HANDLE + (wasm::SPACE_BYTES_PER_HANDLE - 1)] != 0 {
                results.push(handles[i]);
            }
        }
        Ok(results)
    }
}

/// Convenience wrapper for the send half of a channel, to allow use of the
/// [`std::io::Write`] trait.
pub struct SendChannelHalf {
    handle: Handle,
}

impl SendChannelHalf {
    pub fn new(handle: Handle) -> SendChannelHalf {
        SendChannelHalf { handle }
    }

    pub fn write_message(&mut self, buf: &[u8], handles: &[Handle]) -> std::io::Result<()> {
        result_from_status(
            OakStatus::from_i32(unsafe {
                wasm::channel_write(
                    self.handle,
                    buf.as_ptr(),
                    buf.len(),
                    handles.as_ptr() as *const u8, // Wasm spec defines this as little-endian
                    handles.len(),
                )
            }),
            (),
        )
    }
}

impl Write for SendChannelHalf {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        match self.write_message(buf, &[]) {
            Ok(_) => Ok(buf.len()),
            Err(e) => Err(e),
        }
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

/// Return an instance of the [`std::io::Write`] trait that emits messages to
/// the Node's logging channel.
///
/// Assumes that the Node has a pre-configured channel to the logging
/// pseudo-Node that is identified by the default port name (`"log"`).
pub fn logging_channel() -> impl Write {
    let logging_channel = SendChannelHalf::new(channel_find("log"));
    // Only flush logging channel on newlines.
    std::io::LineWriter::new(logging_channel)
}

/// Convenience wrapper for the receive half of a channel.
///
/// This helps when the underlying [`Handle`] is known to be for a receive half.
pub struct ReceiveChannelHalf {
    handle: Handle,
}

impl ReceiveChannelHalf {
    pub fn new(handle: Handle) -> ReceiveChannelHalf {
        ReceiveChannelHalf { handle }
    }

    pub fn read_message(
        &mut self,
        buf: &mut Vec<u8>,
        handles: &mut Vec<Handle>,
    ) -> std::io::Result<()> {
        // Try reading from the channel twice: first with provided vectors, then
        // with vectors that have been resized to meet size requirements.
        for resized in &[false, true] {
            let mut actual_size: u32 = 0;
            let mut actual_handle_count: u32 = 0;
            let status = OakStatus::from_i32(unsafe {
                wasm::channel_read(
                    self.handle,
                    buf.as_mut_ptr(),
                    buf.capacity(),
                    &mut actual_size,
                    handles.as_mut_ptr() as *mut u8,
                    handles.capacity(),
                    &mut actual_handle_count,
                )
            });
            match status {
                Some(OakStatus::OK) => {
                    unsafe {
                        buf.set_len(actual_size as usize);
                        // Handles are written in little-endian order, which matches Wasm spec
                        handles.set_len(actual_handle_count as usize);
                    };
                    return Ok(());
                }
                Some(OakStatus::ERR_BUFFER_TOO_SMALL) => {
                    if *resized {
                        return result_from_status(status, ());
                    }
                    // Can escape the match if buffer is too small and !resized.
                }
                _ => return result_from_status(status, ()),
            }

            // Extend the vector to be large enough for the message
            debug!("Got {}, need {}", buf.capacity(), actual_size);
            if (actual_size as usize) < buf.len() {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "Internal error: provided {} bytes for receive, asked for {}",
                        buf.len(),
                        actual_size
                    ),
                ));
            }
            let extra = (actual_size as usize) - buf.len();
            buf.reserve(extra);
        }
        Err(io::Error::new(io::ErrorKind::Other, "Unreachable reached!"))
    }
}

/// Install a panic hook that logs [panic information].
///
/// Logs panic infomation to the logging channel, if one is set.
///
/// [panic information]: std::panic::PanicInfo
pub fn set_panic_hook() {
    std::panic::set_hook(Box::new(|panic_info| {
        let msg = panic_info
            .payload()
            .downcast_ref::<&str>()
            .unwrap_or(&"<UNKNOWN MESSAGE>");
        let (file, line) = match panic_info.location() {
            Some(location) => (location.file(), location.line()),
            None => ("<UNKNOWN FILE>", 0),
        };
        error!(
            "panic occurred in file '{}' at line {}: {}",
            file, line, msg
        );
    }));
}
<commit_msg>Re-order convenience wrappers<commit_after>//
// Copyright 2018 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

extern crate byteorder;
extern crate fmt;
#[macro_use]
extern crate log;
extern crate protobuf;

use byteorder::WriteBytesExt;
use proto::oak_api::OakStatus;
use protobuf::ProtobufEnum;
use std::io;
use std::io::Write;

pub mod grpc;
pub mod proto;
pub mod storage;
#[cfg(test)]
mod tests;
pub mod wasm;

#[cfg(test)]
#[macro_use]
extern crate assert_matches;

/// Handle used to identify read or write channel halves.
///
/// These handles are used for all host function calls.
pub type Handle = u64;

// Build a chunk of memory that is suitable for passing to wasm::wait_on_channels,
// holding the given collection of channel handles.
fn new_handle_space(handles: &[Handle]) -> Vec<u8> {
    let mut space = Vec::with_capacity(wasm::SPACE_BYTES_PER_HANDLE * handles.len());
    for handle in handles {
        space.write_u64::<byteorder::LittleEndian>(*handle).unwrap();
        space.push(0x00);
    }
    space
}

// Prepare a handle space for polling by clearing all of the message-pending
// indicator bytes.
fn prep_handle_space(space: &mut [u8]) {
    let count = space.len() / 8;
    for i in 0..count {
        space[i * wasm::SPACE_BYTES_PER_HANDLE + (wasm::SPACE_BYTES_PER_HANDLE - 1)] = 0;
    }
}

/// Wait for one or more of the provided handles to become ready for reading
/// from.
///
/// This is a convenience wrapper around the [`wasm::wait_on_channels`] host
/// function. This version is easier to use in Rust but is less efficient
/// (because the notification space is re-created on each invocation).
pub fn wait_on_channels(handles: &[Handle]) -> Result<Vec<Handle>, OakStatus> {
    let mut space = new_handle_space(handles);
    unsafe {
        let status = wasm::wait_on_channels(space.as_mut_ptr(), handles.len() as u32);
        match OakStatus::from_i32(status) {
            Some(OakStatus::OK) => (),
            Some(err) => return Err(err),
            None => return Err(OakStatus::OAK_STATUS_UNSPECIFIED),
        }
        let mut results = Vec::with_capacity(handles.len());
        for i in 0..handles.len() {
            if space[i * wasm::SPACE_BYTES_PER_HANDLE + (wasm::SPACE_BYTES_PER_HANDLE - 1)] != 0 {
                results.push(handles[i]);
            }
        }
        Ok(results)
    }
}

/// Create a new unidirectional channel.
///
/// On success, returns [`Handle`] values for the write and read halves
/// (respectively).
pub fn channel_create() -> Result<(Handle, Handle), OakStatus> {
    let mut write: Handle = 0;
    let mut read: Handle = 0;
    match OakStatus::from_i32(unsafe {
        wasm::channel_create(&mut write as *mut u64, &mut read as *mut u64) // @@@ check endianness
    }) {
        Some(OakStatus::OK) => Ok((write, read)),
        Some(err) => Err(err),
        None => Err(OakStatus::OAK_STATUS_UNSPECIFIED),
    }
}

/// Close the specified channel [`Handle`].
pub fn channel_close(handle: Handle) -> OakStatus {
    match OakStatus::from_i32(unsafe { wasm::channel_close(handle) }) {
        Some(s) => s,
        None => OakStatus::OAK_STATUS_UNSPECIFIED,
    }
}

/// Determine the [`Handle`] for a pre-defined channel, identified by its
/// `port_name`.
pub fn channel_find(port_name: &str) -> Handle {
    unsafe { wasm::channel_find(port_name.as_ptr(), port_name.len()) }
}

/// Map an [`OakStatus`] value to the nearest available [`std::io::Result`].
fn result_from_status<T>(status: Option<OakStatus>, val: T) -> std::io::Result<T> {
    match status {
        Some(OakStatus::OAK_STATUS_UNSPECIFIED) => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unspecified Oak status value",
        )),
        Some(OakStatus::OK) => Ok(val),
        Some(OakStatus::ERR_BAD_HANDLE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Bad handle"))
        }
        Some(OakStatus::ERR_INVALID_ARGS) => Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Invalid arguments",
        )),
        Some(OakStatus::ERR_CHANNEL_CLOSED) => Err(io::Error::new(
            io::ErrorKind::ConnectionReset,
            "Channel closed",
        )),
        Some(OakStatus::ERR_BUFFER_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Buffer too small",
        )),
        Some(OakStatus::ERR_HANDLE_SPACE_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Handle space too small",
        )),
        Some(OakStatus::ERR_OUT_OF_RANGE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Out of range"))
        }
        Some(OakStatus::ERR_INTERNAL) => {
            Err(io::Error::new(io::ErrorKind::Other, "Internal error"))
        }
        None => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unknown Oak status value",
        )),
    }
}
/// Convenience wrapper for the send half of a channel, to allow use of the
/// [`std::io::Write`] trait.
pub struct SendChannelHalf {
    handle: Handle,
}

impl SendChannelHalf {
    pub fn new(handle: Handle) -> SendChannelHalf {
        SendChannelHalf { handle }
    }

    pub fn write_message(&mut self, buf: &[u8], handles: &[Handle]) -> std::io::Result<()> {
        result_from_status(
            OakStatus::from_i32(unsafe {
                wasm::channel_write(
                    self.handle,
                    buf.as_ptr(),
                    buf.len(),
                    handles.as_ptr() as *const u8, // Wasm spec defines this as little-endian
                    handles.len(),
                )
            }),
            (),
        )
    }
}

impl Write for SendChannelHalf {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        match self.write_message(buf, &[]) {
            Ok(_) => Ok(buf.len()),
            Err(e) => Err(e),
        }
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

/// Return an instance of the [`std::io::Write`] trait that emits messages to
/// the Node's logging channel.
///
/// Assumes that the Node has a pre-configured channel to the logging
/// pseudo-Node that is identified by the default port name (`"log"`).
pub fn logging_channel() -> impl Write {
    let logging_channel = SendChannelHalf::new(channel_find("log"));
    // Only flush logging channel on newlines.
    std::io::LineWriter::new(logging_channel)
}

/// Convenience wrapper for the receive half of a channel.
///
/// This helps when the underlying [`Handle`] is known to be for a receive half.
pub struct ReceiveChannelHalf {
    handle: Handle,
}

impl ReceiveChannelHalf {
    pub fn new(handle: Handle) -> ReceiveChannelHalf {
        ReceiveChannelHalf { handle }
    }

    pub fn read_message(
        &mut self,
        buf: &mut Vec<u8>,
        handles: &mut Vec<Handle>,
    ) -> std::io::Result<()> {
        // Try reading from the channel twice: first with provided vectors, then
        // with vectors that have been resized to meet size requirements.
        for resized in &[false, true] {
            let mut actual_size: u32 = 0;
            let mut actual_handle_count: u32 = 0;
            let status = OakStatus::from_i32(unsafe {
                wasm::channel_read(
                    self.handle,
                    buf.as_mut_ptr(),
                    buf.capacity(),
                    &mut actual_size,
                    handles.as_mut_ptr() as *mut u8,
                    handles.capacity(),
                    &mut actual_handle_count,
                )
            });
            match status {
                Some(OakStatus::OK) => {
                    unsafe {
                        buf.set_len(actual_size as usize);
                        // Handles are written in little-endian order, which matches Wasm spec
                        handles.set_len(actual_handle_count as usize);
                    };
                    return Ok(());
                }
                Some(OakStatus::ERR_BUFFER_TOO_SMALL) => {
                    if *resized {
                        return result_from_status(status, ());
                    }
                    // Can escape the match if buffer is too small and !resized.
                }
                _ => return result_from_status(status, ()),
            }

            // Extend the vector to be large enough for the message
            debug!("Got {}, need {}", buf.capacity(), actual_size);
            if (actual_size as usize) < buf.len() {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "Internal error: provided {} bytes for receive, asked for {}",
                        buf.len(),
                        actual_size
                    ),
                ));
            }
            let extra = (actual_size as usize) - buf.len();
            buf.reserve(extra);
        }
        Err(io::Error::new(io::ErrorKind::Other, "Unreachable reached!"))
    }
}

/// Install a panic hook that logs [panic information].
///
/// Logs panic infomation to the logging channel, if one is set.
///
/// [panic information]: std::panic::PanicInfo
pub fn set_panic_hook() {
    std::panic::set_hook(Box::new(|panic_info| {
        let msg = panic_info
            .payload()
            .downcast_ref::<&str>()
            .unwrap_or(&"<UNKNOWN MESSAGE>");
        let (file, line) = match panic_info.location() {
            Some(location) => (location.file(), location.line()),
            None => ("<UNKNOWN FILE>", 0),
        };
        error!(
            "panic occurred in file '{}' at line {}: {}",
            file, line, msg
        );
    }));
}
<|endoftext|>


<commit_before>use num_bigint::BigUint;
use num_integer::Integer;
use num_traits::{One, Zero};
use rand::Rng;
use std::mem;
use std::ops::{Mul, Rem, Shr};

pub fn rand_vec<G: Rng>(rng: &mut G, size: usize) -> Vec<u8> {
    ::std::iter::repeat(())
        .map(|()| rng.gen())
        .take(size)
        .collect()
}

pub fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
    let mut base = base.clone();
    let mut exp = exp.clone();
    let mut result: BigUint = One::one();

    while !exp.is_zero() {
        if exp.is_odd() {
            result = result.mul(&base).rem(modulus);
        }
        exp = exp.shr(1);
        base = (&base).mul(&base).rem(modulus);
    }

    result
}

pub trait ReadSeek: ::std::io::Read + ::std::io::Seek {}
impl<T: ::std::io::Read + ::std::io::Seek> ReadSeek for T {}

pub trait Seq {
    fn next(&self) -> Self;
}

macro_rules! impl_seq {
    ($($ty:ty)*) => { $(
        impl Seq for $ty {
            fn next(&self) -> Self { *self + 1 }
        }
    )* }
}

impl_seq!(u8 u16 u32 u64 usize);

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct SeqGenerator<T: Seq>(T);

impl<T: Seq> SeqGenerator<T> {
    pub fn new(value: T) -> Self {
        SeqGenerator(value)
    }

    pub fn get(&mut self) -> T {
        let value = self.0.next();
        mem::replace(&mut self.0, value)
    }
}
<commit_msg>Make sequences wrap around to avoid overflows<commit_after>use num_bigint::BigUint;
use num_integer::Integer;
use num_traits::{One, Zero};
use rand::Rng;
use std::mem;
use std::ops::{Mul, Rem, Shr};

pub fn rand_vec<G: Rng>(rng: &mut G, size: usize) -> Vec<u8> {
    ::std::iter::repeat(())
        .map(|()| rng.gen())
        .take(size)
        .collect()
}

pub fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
    let mut base = base.clone();
    let mut exp = exp.clone();
    let mut result: BigUint = One::one();

    while !exp.is_zero() {
        if exp.is_odd() {
            result = result.mul(&base).rem(modulus);
        }
        exp = exp.shr(1);
        base = (&base).mul(&base).rem(modulus);
    }

    result
}

pub trait ReadSeek: ::std::io::Read + ::std::io::Seek {}
impl<T: ::std::io::Read + ::std::io::Seek> ReadSeek for T {}

pub trait Seq {
    fn next(&self) -> Self;
}

macro_rules! impl_seq {
    ($($ty:ty)*) => { $(
        impl Seq for $ty {
            fn next(&self) -> Self { (*self).wrapping_add(1) }
        }
    )* }
}

impl_seq!(u8 u16 u32 u64 usize);

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct SeqGenerator<T: Seq>(T);

impl<T: Seq> SeqGenerator<T> {
    pub fn new(value: T) -> Self {
        SeqGenerator(value)
    }

    pub fn get(&mut self) -> T {
        let value = self.0.next();
        mem::replace(&mut self.0, value)
    }
}
<|endoftext|>


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

// Tests parallel codegen - this can fail if the symbol for the anonymous
// closure in `sum` pollutes the second codegen unit from the first.

// compile-flags: -C codegen_units=2

#![feature(core)]
#![feature(iter_arith)]

mod a {
    fn foo() {
        let x = ["a", "bob", "c"];
        let len: usize = x.iter().map(|s| s.len()).sum();
    }
}

mod b {
    fn bar() {
        let x = ["a", "bob", "c"];
        let len: usize = x.iter().map(|s| s.len()).sum();
    }
}

fn main() {
}
<commit_msg>Auto merge of #26532 - dhuseby:fixing_bitrig_test_failure, r=alexcrichton<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Tests parallel codegen - this can fail if the symbol for the anonymous
// closure in `sum` pollutes the second codegen unit from the first.

// ignore-bitrig
// compile-flags: -C codegen_units=2

#![feature(core)]
#![feature(iter_arith)]

mod a {
    fn foo() {
        let x = ["a", "bob", "c"];
        let len: usize = x.iter().map(|s| s.len()).sum();
    }
}

mod b {
    fn bar() {
        let x = ["a", "bob", "c"];
        let len: usize = x.iter().map(|s| s.len()).sum();
    }
}

fn main() {
}
<|endoftext|>