<commit_before>//! IL entity references.
//!
//! Instructions in Cretonne IL need to reference other entities in the function. This can be other
//! parts of the function like extended basic blocks or stack slots, or it can be external entities
//! that are declared in the function preamble in the text format.
//!
//! These entity references in instruction operands are not implemented as Rust references both
//! because Rust's ownership and mutability rules make it difficult, and because 64-bit pointers
//! take up a lot of space, and we want a compact in-memory representation. Instead, entity
//! references are structs wrapping a `u32` index into a table in the `Function` main data
//! structure. There is a separate index type for each entity type, so we don't lose type safety.
//!
//! The `entities` module defines public types for the entity references along with constants
//! representing an invalid reference. We prefer to use `Option<EntityRef>` whenever possible, but
//! unfortunately that type is twice as large as the 32-bit index type on its own. Thus, compact
//! data structures use the sentinen constant, while function arguments and return values prefer
//! the more Rust-like `Option<EntityRef>` variant.
//!
//! The entity references all implement the `Display` trait in a way that matches the textual IL
//! format.

use entity_map::EntityRef;
use std::default::Default;
use std::fmt::{self, Display, Formatter};
use std::u32;

/// An opaque reference to an extended basic block in a function.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct Ebb(u32);

impl EntityRef for Ebb {
    fn new(index: usize) -> Self {
        assert!(index < (u32::MAX as usize));
        Ebb(index as u32)
    }

    fn index(self) -> usize {
        self.0 as usize
    }
}

impl Ebb {
    /// Create a new EBB reference from its number. This corresponds to the ebbNN representation.
    pub fn with_number(n: u32) -> Option<Ebb> {
        if n < u32::MAX { Some(Ebb(n)) } else { None }
    }
}

/// Display an `Ebb` reference as "ebb12".
impl Display for Ebb {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "ebb{}", self.0)
    }
}

/// A guaranteed invalid EBB reference.
pub const NO_EBB: Ebb = Ebb(u32::MAX);

impl Default for Ebb {
    fn default() -> Ebb {
        NO_EBB
    }
}

/// An opaque reference to an instruction in a function.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct Inst(u32);

impl EntityRef for Inst {
    fn new(index: usize) -> Self {
        assert!(index < (u32::MAX as usize));
        Inst(index as u32)
    }

    fn index(self) -> usize {
        self.0 as usize
    }
}

/// Display an `Inst` reference as "inst7".
impl Display for Inst {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "inst{}", self.0)
    }
}

/// A guaranteed invalid instruction reference.
pub const NO_INST: Inst = Inst(u32::MAX);

impl Default for Inst {
    fn default() -> Inst {
        NO_INST
    }
}

/// An opaque reference to an SSA value.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Value(u32);

// Value references can either reference an instruction directly, or they can refer to the extended
// value table.
pub enum ExpandedValue {
    // This is the first value produced by the referenced instruction.
    Direct(Inst),

    // This value is described in the extended value table.
    Table(usize),

    // This is NO_VALUE.
    None,
}

impl Value {
    /// Create a `Direct` value from its number representation.
    /// This is the number in the vNN notation.
    pub fn direct_with_number(n: u32) -> Option<Value> {
        if n < u32::MAX / 2 {
            let encoding = n * 2;
            assert!(encoding < u32::MAX);
            Some(Value(encoding))
        } else {
            None
        }
    }

    /// Create a `Table` value from its number representation.
    /// This is the number in the vxNN notation.
    pub fn table_with_number(n: u32) -> Option<Value> {
        if n < u32::MAX / 2 {
            let encoding = n * 2 + 1;
            assert!(encoding < u32::MAX);
            Some(Value(encoding))
        } else {
            None
        }
    }
    pub fn new_direct(i: Inst) -> Value {
        let encoding = i.index() * 2;
        assert!(encoding < u32::MAX as usize);
        Value(encoding as u32)
    }

    pub fn new_table(index: usize) -> Value {
        let encoding = index * 2 + 1;
        assert!(encoding < u32::MAX as usize);
        Value(encoding as u32)
    }

    // Expand the internal representation into something useful.
    pub fn expand(&self) -> ExpandedValue {
        use self::ExpandedValue::*;
        if *self == NO_VALUE {
            return None;
        }
        let index = (self.0 / 2) as usize;
        if self.0 % 2 == 0 {
            Direct(Inst::new(index))
        } else {
            Table(index)
        }
    }
}

/// Display a `Value` reference as "v7" or "v2x".
impl Display for Value {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        use self::ExpandedValue::*;
        match self.expand() {
            Direct(i) => write!(fmt, "v{}", i.0),
            Table(i) => write!(fmt, "vx{}", i),
            None => write!(fmt, "NO_VALUE"),
        }
    }
}

/// A guaranteed invalid value reference.
pub const NO_VALUE: Value = Value(u32::MAX);

impl Default for Value {
    fn default() -> Value {
        NO_VALUE
    }
}

/// An opaque reference to a stack slot.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct StackSlot(u32);

impl EntityRef for StackSlot {
    fn new(index: usize) -> StackSlot {
        assert!(index < (u32::MAX as usize));
        StackSlot(index as u32)
    }

    fn index(self) -> usize {
        self.0 as usize
    }
}

/// Display a `StackSlot` reference as "ss12".
impl Display for StackSlot {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "ss{}", self.0)
    }
}

/// A guaranteed invalid stack slot reference.
pub const NO_STACK_SLOT: StackSlot = StackSlot(u32::MAX);

impl Default for StackSlot {
    fn default() -> StackSlot {
        NO_STACK_SLOT
    }
}

/// An opaque reference to a jump table.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct JumpTable(u32);

impl EntityRef for JumpTable {
    fn new(index: usize) -> JumpTable {
        assert!(index < (u32::MAX as usize));
        JumpTable(index as u32)
    }

    fn index(self) -> usize {
        self.0 as usize
    }
}

/// Display a `JumpTable` reference as "jt12".
impl Display for JumpTable {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "jt{}", self.0)
    }
}

/// A guaranteed invalid jump table reference.
pub const NO_JUMP_TABLE: JumpTable = JumpTable(u32::MAX);

impl Default for JumpTable {
    fn default() -> JumpTable {
        NO_JUMP_TABLE
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::u32;
    use entity_map::EntityRef;

    #[test]
    fn value_with_number() {
        assert_eq!(Value::direct_with_number(0).unwrap().to_string(), "v0");
        assert_eq!(Value::direct_with_number(1).unwrap().to_string(), "v1");
        assert_eq!(Value::table_with_number(0).unwrap().to_string(), "vx0");
        assert_eq!(Value::table_with_number(1).unwrap().to_string(), "vx1");

        assert_eq!(Value::direct_with_number(u32::MAX / 2), None);
        assert_eq!(match Value::direct_with_number(u32::MAX / 2 - 1).unwrap().expand() {
                       ExpandedValue::Direct(i) => i.index() as u32,
                       _ => u32::MAX,
                   },
                   u32::MAX / 2 - 1);

        assert_eq!(Value::table_with_number(u32::MAX / 2), None);
        assert_eq!(match Value::table_with_number(u32::MAX / 2 - 1).unwrap().expand() {
                       ExpandedValue::Table(i) => i as u32,
                       _ => u32::MAX,
                   },
                   u32::MAX / 2 - 1);
    }
}
<commit_msg>Add an AnyEntity enum type.<commit_after>//! IL entity references.
//!
//! Instructions in Cretonne IL need to reference other entities in the function. This can be other
//! parts of the function like extended basic blocks or stack slots, or it can be external entities
//! that are declared in the function preamble in the text format.
//!
//! These entity references in instruction operands are not implemented as Rust references both
//! because Rust's ownership and mutability rules make it difficult, and because 64-bit pointers
//! take up a lot of space, and we want a compact in-memory representation. Instead, entity
//! references are structs wrapping a `u32` index into a table in the `Function` main data
//! structure. There is a separate index type for each entity type, so we don't lose type safety.
//!
//! The `entities` module defines public types for the entity references along with constants
//! representing an invalid reference. We prefer to use `Option<EntityRef>` whenever possible, but
//! unfortunately that type is twice as large as the 32-bit index type on its own. Thus, compact
//! data structures use the sentinen constant, while function arguments and return values prefer
//! the more Rust-like `Option<EntityRef>` variant.
//!
//! The entity references all implement the `Display` trait in a way that matches the textual IL
//! format.

use entity_map::EntityRef;
use std::default::Default;
use std::fmt::{self, Display, Formatter};
use std::u32;

/// An opaque reference to an extended basic block in a function.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct Ebb(u32);

impl EntityRef for Ebb {
    fn new(index: usize) -> Self {
        assert!(index < (u32::MAX as usize));
        Ebb(index as u32)
    }

    fn index(self) -> usize {
        self.0 as usize
    }
}

impl Ebb {
    /// Create a new EBB reference from its number. This corresponds to the ebbNN representation.
    pub fn with_number(n: u32) -> Option<Ebb> {
        if n < u32::MAX { Some(Ebb(n)) } else { None }
    }
}

/// Display an `Ebb` reference as "ebb12".
impl Display for Ebb {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "ebb{}", self.0)
    }
}

/// A guaranteed invalid EBB reference.
pub const NO_EBB: Ebb = Ebb(u32::MAX);

impl Default for Ebb {
    fn default() -> Ebb {
        NO_EBB
    }
}

/// An opaque reference to an instruction in a function.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct Inst(u32);

impl EntityRef for Inst {
    fn new(index: usize) -> Self {
        assert!(index < (u32::MAX as usize));
        Inst(index as u32)
    }

    fn index(self) -> usize {
        self.0 as usize
    }
}

/// Display an `Inst` reference as "inst7".
impl Display for Inst {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "inst{}", self.0)
    }
}

/// A guaranteed invalid instruction reference.
pub const NO_INST: Inst = Inst(u32::MAX);

impl Default for Inst {
    fn default() -> Inst {
        NO_INST
    }
}

/// An opaque reference to an SSA value.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Value(u32);

// Value references can either reference an instruction directly, or they can refer to the extended
// value table.
pub enum ExpandedValue {
    // This is the first value produced by the referenced instruction.
    Direct(Inst),

    // This value is described in the extended value table.
    Table(usize),

    // This is NO_VALUE.
    None,
}

impl Value {
    /// Create a `Direct` value from its number representation.
    /// This is the number in the vNN notation.
    pub fn direct_with_number(n: u32) -> Option<Value> {
        if n < u32::MAX / 2 {
            let encoding = n * 2;
            assert!(encoding < u32::MAX);
            Some(Value(encoding))
        } else {
            None
        }
    }

    /// Create a `Table` value from its number representation.
    /// This is the number in the vxNN notation.
    pub fn table_with_number(n: u32) -> Option<Value> {
        if n < u32::MAX / 2 {
            let encoding = n * 2 + 1;
            assert!(encoding < u32::MAX);
            Some(Value(encoding))
        } else {
            None
        }
    }
    pub fn new_direct(i: Inst) -> Value {
        let encoding = i.index() * 2;
        assert!(encoding < u32::MAX as usize);
        Value(encoding as u32)
    }

    pub fn new_table(index: usize) -> Value {
        let encoding = index * 2 + 1;
        assert!(encoding < u32::MAX as usize);
        Value(encoding as u32)
    }

    // Expand the internal representation into something useful.
    pub fn expand(&self) -> ExpandedValue {
        use self::ExpandedValue::*;
        if *self == NO_VALUE {
            return None;
        }
        let index = (self.0 / 2) as usize;
        if self.0 % 2 == 0 {
            Direct(Inst::new(index))
        } else {
            Table(index)
        }
    }
}

/// Display a `Value` reference as "v7" or "v2x".
impl Display for Value {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        use self::ExpandedValue::*;
        match self.expand() {
            Direct(i) => write!(fmt, "v{}", i.0),
            Table(i) => write!(fmt, "vx{}", i),
            None => write!(fmt, "NO_VALUE"),
        }
    }
}

/// A guaranteed invalid value reference.
pub const NO_VALUE: Value = Value(u32::MAX);

impl Default for Value {
    fn default() -> Value {
        NO_VALUE
    }
}

/// An opaque reference to a stack slot.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct StackSlot(u32);

impl EntityRef for StackSlot {
    fn new(index: usize) -> StackSlot {
        assert!(index < (u32::MAX as usize));
        StackSlot(index as u32)
    }

    fn index(self) -> usize {
        self.0 as usize
    }
}

/// Display a `StackSlot` reference as "ss12".
impl Display for StackSlot {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "ss{}", self.0)
    }
}

/// A guaranteed invalid stack slot reference.
pub const NO_STACK_SLOT: StackSlot = StackSlot(u32::MAX);

impl Default for StackSlot {
    fn default() -> StackSlot {
        NO_STACK_SLOT
    }
}

/// An opaque reference to a jump table.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct JumpTable(u32);

impl EntityRef for JumpTable {
    fn new(index: usize) -> JumpTable {
        assert!(index < (u32::MAX as usize));
        JumpTable(index as u32)
    }

    fn index(self) -> usize {
        self.0 as usize
    }
}

/// Display a `JumpTable` reference as "jt12".
impl Display for JumpTable {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "jt{}", self.0)
    }
}

/// A guaranteed invalid jump table reference.
pub const NO_JUMP_TABLE: JumpTable = JumpTable(u32::MAX);

impl Default for JumpTable {
    fn default() -> JumpTable {
        NO_JUMP_TABLE
    }
}

/// A reference to any of the entities defined in this module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum AnyEntity {
    /// The whole function.
    Function,
    Ebb(Ebb),
    Inst(Inst),
    Value(Value),
    StackSlot(StackSlot),
    JumpTable(JumpTable),
}

impl Display for AnyEntity {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match *self {
            AnyEntity::Function => write!(fmt, "function"),
            AnyEntity::Ebb(r) => r.fmt(fmt),
            AnyEntity::Inst(r) => r.fmt(fmt),
            AnyEntity::Value(r) => r.fmt(fmt),
            AnyEntity::StackSlot(r) => r.fmt(fmt),
            AnyEntity::JumpTable(r) => r.fmt(fmt),
        }
    }
}

impl From<Ebb> for AnyEntity {
    fn from(r: Ebb) -> AnyEntity {
        AnyEntity::Ebb(r)
    }
}

impl From<Inst> for AnyEntity {
    fn from(r: Inst) -> AnyEntity {
        AnyEntity::Inst(r)
    }
}

impl From<Value> for AnyEntity {
    fn from(r: Value) -> AnyEntity {
        AnyEntity::Value(r)
    }
}

impl From<StackSlot> for AnyEntity {
    fn from(r: StackSlot) -> AnyEntity {
        AnyEntity::StackSlot(r)
    }
}

impl From<JumpTable> for AnyEntity {
    fn from(r: JumpTable) -> AnyEntity {
        AnyEntity::JumpTable(r)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::u32;
    use entity_map::EntityRef;

    #[test]
    fn value_with_number() {
        assert_eq!(Value::direct_with_number(0).unwrap().to_string(), "v0");
        assert_eq!(Value::direct_with_number(1).unwrap().to_string(), "v1");
        assert_eq!(Value::table_with_number(0).unwrap().to_string(), "vx0");
        assert_eq!(Value::table_with_number(1).unwrap().to_string(), "vx1");

        assert_eq!(Value::direct_with_number(u32::MAX / 2), None);
        assert_eq!(match Value::direct_with_number(u32::MAX / 2 - 1).unwrap().expand() {
                       ExpandedValue::Direct(i) => i.index() as u32,
                       _ => u32::MAX,
                   },
                   u32::MAX / 2 - 1);

        assert_eq!(Value::table_with_number(u32::MAX / 2), None);
        assert_eq!(match Value::table_with_number(u32::MAX / 2 - 1).unwrap().expand() {
                       ExpandedValue::Table(i) => i as u32,
                       _ => u32::MAX,
                   },
                   u32::MAX / 2 - 1);
    }
}
<|endoftext|>


<commit_before>//! This module implements some validity checks for attributes.
//! In particular it verifies that `#[inline]` and `#[repr]` attributes are
//! attached to items that actually support them and if there are
//! conflicts between multiple such attributes attached to the same
//! item.

use crate::ty::TyCtxt;
use crate::ty::query::Providers;

use crate::hir;
use crate::hir::def_id::DefId;
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
use std::fmt::{self, Display};
use syntax::symbol::sym;
use syntax_pos::Span;

#[derive(Copy, Clone, PartialEq)]
pub(crate) enum Target {
    ExternCrate,
    Use,
    Static,
    Const,
    Fn,
    Closure,
    Mod,
    ForeignMod,
    GlobalAsm,
    Ty,
    OpaqueTy,
    Enum,
    Struct,
    Union,
    Trait,
    TraitAlias,
    Impl,
    Expression,
    Statement,
}

impl Display for Target {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", match *self {
            Target::ExternCrate => "extern crate",
            Target::Use => "use",
            Target::Static => "static item",
            Target::Const => "constant item",
            Target::Fn => "function",
            Target::Closure => "closure",
            Target::Mod => "module",
            Target::ForeignMod => "foreign module",
            Target::GlobalAsm => "global asm",
            Target::Ty => "type alias",
            Target::OpaqueTy => "opaque type",
            Target::Enum => "enum",
            Target::Struct => "struct",
            Target::Union => "union",
            Target::Trait => "trait",
            Target::TraitAlias => "trait alias",
            Target::Impl => "item",
            Target::Expression => "expression",
            Target::Statement => "statement",
        })
    }
}

impl Target {
    pub(crate) fn from_item(item: &hir::Item) -> Target {
        match item.node {
            hir::ItemKind::ExternCrate(..) => Target::ExternCrate,
            hir::ItemKind::Use(..) => Target::Use,
            hir::ItemKind::Static(..) => Target::Static,
            hir::ItemKind::Const(..) => Target::Const,
            hir::ItemKind::Fn(..) => Target::Fn,
            hir::ItemKind::Mod(..) => Target::Mod,
            hir::ItemKind::ForeignMod(..) => Target::ForeignMod,
            hir::ItemKind::GlobalAsm(..) => Target::GlobalAsm,
            hir::ItemKind::TyAlias(..) => Target::TyAlias,
            hir::ItemKind::OpaqueTy(..) => Target::OpaqueTy,
            hir::ItemKind::Enum(..) => Target::Enum,
            hir::ItemKind::Struct(..) => Target::Struct,
            hir::ItemKind::Union(..) => Target::Union,
            hir::ItemKind::Trait(..) => Target::Trait,
            hir::ItemKind::TraitAlias(..) => Target::TraitAlias,
            hir::ItemKind::Impl(..) => Target::Impl,
        }
    }
}

struct CheckAttrVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
}

impl CheckAttrVisitor<'tcx> {
    /// Checks any attribute.
    fn check_attributes(&self, item: &hir::Item, target: Target) {
        if target == Target::Fn || target == Target::Const {
            self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(item.hir_id));
        } else if let Some(a) = item.attrs.iter().find(|a| a.check_name(sym::target_feature)) {
            self.tcx.sess.struct_span_err(a.span, "attribute should be applied to a function")
                .span_label(item.span, "not a function")
                .emit();
        }

        for attr in &item.attrs {
            if attr.check_name(sym::inline) {
                self.check_inline(attr, &item.span, target)
            } else if attr.check_name(sym::non_exhaustive) {
                self.check_non_exhaustive(attr, item, target)
            } else if attr.check_name(sym::marker) {
                self.check_marker(attr, item, target)
            }
        }

        self.check_repr(item, target);
        self.check_used(item, target);
    }

    /// Checks if an `#[inline]` is applied to a function or a closure.
    fn check_inline(&self, attr: &hir::Attribute, span: &Span, target: Target) {
        if target != Target::Fn && target != Target::Closure {
            struct_span_err!(self.tcx.sess,
                             attr.span,
                             E0518,
                             "attribute should be applied to function or closure")
                .span_label(*span, "not a function or closure")
                .emit();
        }
    }

    /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.
    fn check_non_exhaustive(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
        match target {
            Target::Struct | Target::Enum => { /* Valid */ },
            _ => {
                struct_span_err!(self.tcx.sess,
                                 attr.span,
                                 E0701,
                                 "attribute can only be applied to a struct or enum")
                    .span_label(item.span, "not a struct or enum")
                    .emit();
                return;
            }
        }
    }

    /// Checks if the `#[marker]` attribute on an `item` is valid.
    fn check_marker(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
        match target {
            Target::Trait => { /* Valid */ },
            _ => {
                self.tcx.sess
                    .struct_span_err(attr.span, "attribute can only be applied to a trait")
                    .span_label(item.span, "not a trait")
                    .emit();
                return;
            }
        }
    }

    /// Checks if the `#[repr]` attributes on `item` are valid.
    fn check_repr(&self, item: &hir::Item, target: Target) {
        // Extract the names of all repr hints, e.g., [foo, bar, align] for:
        // ```
        // #[repr(foo)]
        // #[repr(bar, align(8))]
        // ```
        let hints: Vec<_> = item.attrs
            .iter()
            .filter(|attr| attr.check_name(sym::repr))
            .filter_map(|attr| attr.meta_item_list())
            .flatten()
            .collect();

        let mut int_reprs = 0;
        let mut is_c = false;
        let mut is_simd = false;
        let mut is_transparent = false;

        for hint in &hints {
            let (article, allowed_targets) = match hint.name_or_empty() {
                name @ sym::C | name @ sym::align => {
                    is_c |= name == sym::C;
                    match target {
                        Target::Struct | Target::Union | Target::Enum => continue,
                        _ => ("a", "struct, enum, or union"),
                    }
                }
                sym::packed => {
                    if target != Target::Struct &&
                            target != Target::Union {
                                ("a", "struct or union")
                    } else {
                        continue
                    }
                }
                sym::simd => {
                    is_simd = true;
                    if target != Target::Struct {
                        ("a", "struct")
                    } else {
                        continue
                    }
                }
                sym::transparent => {
                    is_transparent = true;
                    match target {
                        Target::Struct | Target::Union | Target::Enum => continue,
                        _ => ("a", "struct, enum, or union"),
                    }
                }
                sym::i8  | sym::u8  | sym::i16 | sym::u16 |
                sym::i32 | sym::u32 | sym::i64 | sym::u64 |
                sym::isize | sym::usize => {
                    int_reprs += 1;
                    if target != Target::Enum {
                        ("an", "enum")
                    } else {
                        continue
                    }
                }
                _ => continue,
            };
            self.emit_repr_error(
                hint.span(),
                item.span,
                &format!("attribute should be applied to {}", allowed_targets),
                &format!("not {} {}", article, allowed_targets),
            )
        }

        // Just point at all repr hints if there are any incompatibilities.
        // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
        let hint_spans = hints.iter().map(|hint| hint.span());

        // Error on repr(transparent, <anything else>).
        if is_transparent && hints.len() > 1 {
            let hint_spans: Vec<_> = hint_spans.clone().collect();
            span_err!(self.tcx.sess, hint_spans, E0692,
                      "transparent {} cannot have other repr hints", target);
        }
        // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
        if (int_reprs > 1)
           || (is_simd && is_c)
           || (int_reprs == 1 && is_c && is_c_like_enum(item)) {
            let hint_spans: Vec<_> = hint_spans.collect();
            span_warn!(self.tcx.sess, hint_spans, E0566,
                       "conflicting representation hints");
        }
    }

    fn emit_repr_error(
        &self,
        hint_span: Span,
        label_span: Span,
        hint_message: &str,
        label_message: &str,
    ) {
        struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
            .span_label(label_span, label_message)
            .emit();
    }

    fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
        // When checking statements ignore expressions, they will be checked later
        if let hir::StmtKind::Local(ref l) = stmt.node {
            for attr in l.attrs.iter() {
                if attr.check_name(sym::inline) {
                    self.check_inline(attr, &stmt.span, Target::Statement);
                }
                if attr.check_name(sym::repr) {
                    self.emit_repr_error(
                        attr.span,
                        stmt.span,
                        "attribute should not be applied to a statement",
                        "not a struct, enum, or union",
                    );
                }
            }
        }
    }

    fn check_expr_attributes(&self, expr: &hir::Expr) {
        let target = match expr.node {
            hir::ExprKind::Closure(..) => Target::Closure,
            _ => Target::Expression,
        };
        for attr in expr.attrs.iter() {
            if attr.check_name(sym::inline) {
                self.check_inline(attr, &expr.span, target);
            }
            if attr.check_name(sym::repr) {
                self.emit_repr_error(
                    attr.span,
                    expr.span,
                    "attribute should not be applied to an expression",
                    "not defining a struct, enum, or union",
                );
            }
        }
    }

    fn check_used(&self, item: &hir::Item, target: Target) {
        for attr in &item.attrs {
            if attr.check_name(sym::used) && target != Target::Static {
                self.tcx.sess
                    .span_err(attr.span, "attribute must be applied to a `static` variable");
            }
        }
    }
}

impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.tcx.hir())
    }

    fn visit_item(&mut self, item: &'tcx hir::Item) {
        let target = Target::from_item(item);
        self.check_attributes(item, target);
        intravisit::walk_item(self, item)
    }

    fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
        self.check_stmt_attributes(stmt);
        intravisit::walk_stmt(self, stmt)
    }

    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        self.check_expr_attributes(expr);
        intravisit::walk_expr(self, expr)
    }
}

fn is_c_like_enum(item: &hir::Item) -> bool {
    if let hir::ItemKind::Enum(ref def, _) = item.node {
        for variant in &def.variants {
            match variant.node.data {
                hir::VariantData::Unit(..) => { /* continue */ }
                _ => { return false; }
            }
        }
        true
    } else {
        false
    }
}

fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: DefId) {
    tcx.hir().visit_item_likes_in_module(
        module_def_id,
        &mut CheckAttrVisitor { tcx }.as_deep_visitor()
    );
}

pub(crate) fn provide(providers: &mut Providers<'_>) {
    *providers = Providers {
        check_mod_attrs,
        ..*providers
    };
}
<commit_msg>Rename `Target::Ty` to `Target::TyAlias`<commit_after>//! This module implements some validity checks for attributes.
//! In particular it verifies that `#[inline]` and `#[repr]` attributes are
//! attached to items that actually support them and if there are
//! conflicts between multiple such attributes attached to the same
//! item.

use crate::ty::TyCtxt;
use crate::ty::query::Providers;

use crate::hir;
use crate::hir::def_id::DefId;
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
use std::fmt::{self, Display};
use syntax::symbol::sym;
use syntax_pos::Span;

#[derive(Copy, Clone, PartialEq)]
pub(crate) enum Target {
    ExternCrate,
    Use,
    Static,
    Const,
    Fn,
    Closure,
    Mod,
    ForeignMod,
    GlobalAsm,
    TyAlias,
    OpaqueTy,
    Enum,
    Struct,
    Union,
    Trait,
    TraitAlias,
    Impl,
    Expression,
    Statement,
}

impl Display for Target {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", match *self {
            Target::ExternCrate => "extern crate",
            Target::Use => "use",
            Target::Static => "static item",
            Target::Const => "constant item",
            Target::Fn => "function",
            Target::Closure => "closure",
            Target::Mod => "module",
            Target::ForeignMod => "foreign module",
            Target::GlobalAsm => "global asm",
            Target::TyAlias => "type alias",
            Target::OpaqueTy => "opaque type",
            Target::Enum => "enum",
            Target::Struct => "struct",
            Target::Union => "union",
            Target::Trait => "trait",
            Target::TraitAlias => "trait alias",
            Target::Impl => "item",
            Target::Expression => "expression",
            Target::Statement => "statement",
        })
    }
}

impl Target {
    pub(crate) fn from_item(item: &hir::Item) -> Target {
        match item.node {
            hir::ItemKind::ExternCrate(..) => Target::ExternCrate,
            hir::ItemKind::Use(..) => Target::Use,
            hir::ItemKind::Static(..) => Target::Static,
            hir::ItemKind::Const(..) => Target::Const,
            hir::ItemKind::Fn(..) => Target::Fn,
            hir::ItemKind::Mod(..) => Target::Mod,
            hir::ItemKind::ForeignMod(..) => Target::ForeignMod,
            hir::ItemKind::GlobalAsm(..) => Target::GlobalAsm,
            hir::ItemKind::TyAlias(..) => Target::TyAlias,
            hir::ItemKind::OpaqueTy(..) => Target::OpaqueTy,
            hir::ItemKind::Enum(..) => Target::Enum,
            hir::ItemKind::Struct(..) => Target::Struct,
            hir::ItemKind::Union(..) => Target::Union,
            hir::ItemKind::Trait(..) => Target::Trait,
            hir::ItemKind::TraitAlias(..) => Target::TraitAlias,
            hir::ItemKind::Impl(..) => Target::Impl,
        }
    }
}

struct CheckAttrVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
}

impl CheckAttrVisitor<'tcx> {
    /// Checks any attribute.
    fn check_attributes(&self, item: &hir::Item, target: Target) {
        if target == Target::Fn || target == Target::Const {
            self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(item.hir_id));
        } else if let Some(a) = item.attrs.iter().find(|a| a.check_name(sym::target_feature)) {
            self.tcx.sess.struct_span_err(a.span, "attribute should be applied to a function")
                .span_label(item.span, "not a function")
                .emit();
        }

        for attr in &item.attrs {
            if attr.check_name(sym::inline) {
                self.check_inline(attr, &item.span, target)
            } else if attr.check_name(sym::non_exhaustive) {
                self.check_non_exhaustive(attr, item, target)
            } else if attr.check_name(sym::marker) {
                self.check_marker(attr, item, target)
            }
        }

        self.check_repr(item, target);
        self.check_used(item, target);
    }

    /// Checks if an `#[inline]` is applied to a function or a closure.
    fn check_inline(&self, attr: &hir::Attribute, span: &Span, target: Target) {
        if target != Target::Fn && target != Target::Closure {
            struct_span_err!(self.tcx.sess,
                             attr.span,
                             E0518,
                             "attribute should be applied to function or closure")
                .span_label(*span, "not a function or closure")
                .emit();
        }
    }

    /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.
    fn check_non_exhaustive(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
        match target {
            Target::Struct | Target::Enum => { /* Valid */ },
            _ => {
                struct_span_err!(self.tcx.sess,
                                 attr.span,
                                 E0701,
                                 "attribute can only be applied to a struct or enum")
                    .span_label(item.span, "not a struct or enum")
                    .emit();
                return;
            }
        }
    }

    /// Checks if the `#[marker]` attribute on an `item` is valid.
    fn check_marker(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
        match target {
            Target::Trait => { /* Valid */ },
            _ => {
                self.tcx.sess
                    .struct_span_err(attr.span, "attribute can only be applied to a trait")
                    .span_label(item.span, "not a trait")
                    .emit();
                return;
            }
        }
    }

    /// Checks if the `#[repr]` attributes on `item` are valid.
    fn check_repr(&self, item: &hir::Item, target: Target) {
        // Extract the names of all repr hints, e.g., [foo, bar, align] for:
        // ```
        // #[repr(foo)]
        // #[repr(bar, align(8))]
        // ```
        let hints: Vec<_> = item.attrs
            .iter()
            .filter(|attr| attr.check_name(sym::repr))
            .filter_map(|attr| attr.meta_item_list())
            .flatten()
            .collect();

        let mut int_reprs = 0;
        let mut is_c = false;
        let mut is_simd = false;
        let mut is_transparent = false;

        for hint in &hints {
            let (article, allowed_targets) = match hint.name_or_empty() {
                name @ sym::C | name @ sym::align => {
                    is_c |= name == sym::C;
                    match target {
                        Target::Struct | Target::Union | Target::Enum => continue,
                        _ => ("a", "struct, enum, or union"),
                    }
                }
                sym::packed => {
                    if target != Target::Struct &&
                            target != Target::Union {
                                ("a", "struct or union")
                    } else {
                        continue
                    }
                }
                sym::simd => {
                    is_simd = true;
                    if target != Target::Struct {
                        ("a", "struct")
                    } else {
                        continue
                    }
                }
                sym::transparent => {
                    is_transparent = true;
                    match target {
                        Target::Struct | Target::Union | Target::Enum => continue,
                        _ => ("a", "struct, enum, or union"),
                    }
                }
                sym::i8  | sym::u8  | sym::i16 | sym::u16 |
                sym::i32 | sym::u32 | sym::i64 | sym::u64 |
                sym::isize | sym::usize => {
                    int_reprs += 1;
                    if target != Target::Enum {
                        ("an", "enum")
                    } else {
                        continue
                    }
                }
                _ => continue,
            };
            self.emit_repr_error(
                hint.span(),
                item.span,
                &format!("attribute should be applied to {}", allowed_targets),
                &format!("not {} {}", article, allowed_targets),
            )
        }

        // Just point at all repr hints if there are any incompatibilities.
        // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
        let hint_spans = hints.iter().map(|hint| hint.span());

        // Error on repr(transparent, <anything else>).
        if is_transparent && hints.len() > 1 {
            let hint_spans: Vec<_> = hint_spans.clone().collect();
            span_err!(self.tcx.sess, hint_spans, E0692,
                      "transparent {} cannot have other repr hints", target);
        }
        // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
        if (int_reprs > 1)
           || (is_simd && is_c)
           || (int_reprs == 1 && is_c && is_c_like_enum(item)) {
            let hint_spans: Vec<_> = hint_spans.collect();
            span_warn!(self.tcx.sess, hint_spans, E0566,
                       "conflicting representation hints");
        }
    }

    fn emit_repr_error(
        &self,
        hint_span: Span,
        label_span: Span,
        hint_message: &str,
        label_message: &str,
    ) {
        struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
            .span_label(label_span, label_message)
            .emit();
    }

    fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
        // When checking statements ignore expressions, they will be checked later
        if let hir::StmtKind::Local(ref l) = stmt.node {
            for attr in l.attrs.iter() {
                if attr.check_name(sym::inline) {
                    self.check_inline(attr, &stmt.span, Target::Statement);
                }
                if attr.check_name(sym::repr) {
                    self.emit_repr_error(
                        attr.span,
                        stmt.span,
                        "attribute should not be applied to a statement",
                        "not a struct, enum, or union",
                    );
                }
            }
        }
    }

    fn check_expr_attributes(&self, expr: &hir::Expr) {
        let target = match expr.node {
            hir::ExprKind::Closure(..) => Target::Closure,
            _ => Target::Expression,
        };
        for attr in expr.attrs.iter() {
            if attr.check_name(sym::inline) {
                self.check_inline(attr, &expr.span, target);
            }
            if attr.check_name(sym::repr) {
                self.emit_repr_error(
                    attr.span,
                    expr.span,
                    "attribute should not be applied to an expression",
                    "not defining a struct, enum, or union",
                );
            }
        }
    }

    fn check_used(&self, item: &hir::Item, target: Target) {
        for attr in &item.attrs {
            if attr.check_name(sym::used) && target != Target::Static {
                self.tcx.sess
                    .span_err(attr.span, "attribute must be applied to a `static` variable");
            }
        }
    }
}

impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.tcx.hir())
    }

    fn visit_item(&mut self, item: &'tcx hir::Item) {
        let target = Target::from_item(item);
        self.check_attributes(item, target);
        intravisit::walk_item(self, item)
    }

    fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
        self.check_stmt_attributes(stmt);
        intravisit::walk_stmt(self, stmt)
    }

    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        self.check_expr_attributes(expr);
        intravisit::walk_expr(self, expr)
    }
}

fn is_c_like_enum(item: &hir::Item) -> bool {
    if let hir::ItemKind::Enum(ref def, _) = item.node {
        for variant in &def.variants {
            match variant.node.data {
                hir::VariantData::Unit(..) => { /* continue */ }
                _ => { return false; }
            }
        }
        true
    } else {
        false
    }
}

fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: DefId) {
    tcx.hir().visit_item_likes_in_module(
        module_def_id,
        &mut CheckAttrVisitor { tcx }.as_deep_visitor()
    );
}

pub(crate) fn provide(providers: &mut Providers<'_>) {
    *providers = Providers {
        check_mod_attrs,
        ..*providers
    };
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate rustfbp;
use rustfbp::component::*;

extern crate capnp;

mod contract_capnp {
    include!("fbp_graph.rs");
    include!("fbp_lexical.rs");
    include!("fbp_semantic_error.rs");
}
use contract_capnp::graph;
use contract_capnp::lexical;
use contract_capnp::semantic_error;

#[derive(Debug)]
struct Graph {
    nodes: Vec<(String, String)>,
    edges: Vec<(String, String, String, String, String, String)>,
    iips: Vec<(String, String, String, String)>,
    ext_in: Vec<(String, String, String, String)>,
    ext_out: Vec<(String, String, String, String)>,
}

#[derive(PartialEq, Debug)]
enum State {
    Break, Comp, Port, IIP,
    CompPort, CompPortBind, CompPortBindPort,
    CompPortExternal, CompPortExternalPort,
    PortExternal, PortExternalPort,
    IIPBind, IIPBindPort,
    Error
}
use State::*;

enum Literal {
    Comp(String, String),
    Port(String, String),
    IIP(String),
    Bind, External,
}

component! {
    fbp_semantic,
    inputs(input: fbp_lexical),
    inputs_array(),
    outputs(output: fbp_graph, error: fbp_semantic_error),
    outputs_array(),
    option(),
    acc(),
    fn run(&mut self) {
        let mut ip = self.ports.recv("input".into()).expect("fbp_semantic : unable to receive");
        let literal = ip.get_reader().expect("fbp_semantic : cannot get reader");
        let literal: lexical::Reader = literal.get_root().expect("fbp_semantic : not a literal");
        let literal = literal.which().expect("fbp_semantic : cannot which");

        match literal {
            lexical::Start(path) => {
                match handle_stream(&self) {
                    Ok(graph) => { send_graph(&self, path.expect("not a path"), &graph) },
                    Err(errors) => {
                        let mut new_ip = capnp::message::Builder::new_default();
                        {
                            let mut ip = new_ip.init_root::<semantic_error::Builder>();
                            ip.set_path(path.unwrap());
                            {
                                let mut nodes = ip.init_parsing(errors.len() as u32);
                                let mut i = 0;
                                for n in &errors {
                                    nodes.borrow().set(i, &n[..]);
                                    i += 1;
                                }
                            }
                        }
                        let mut send_ip = self.allocator.ip.build_empty();
                        send_ip.write_builder(&new_ip).expect("fbp_lexical: cannot write");
                        let _ = self.ports.send("error".into(), send_ip);
                    },
                }
            }
            _ => { panic!("bad stream") },
        }
    }
}

fn handle_stream(comp: &fbp_semantic) -> std::result::Result<Graph, Vec<String>> {
    let mut state = Break;
    let mut stack: Vec<Literal> = vec![];
    let mut graph = Graph {
        nodes: vec![],
        edges: vec![],
        iips: vec![],
        ext_in: vec![],
        ext_out: vec![]
    };
    let mut errors: Vec<String> = vec![];
    let mut line: usize = 1;

    loop {

        let mut ip = comp.ports.recv("input".into()).expect("fbp_semantic : unable to receive");
        let literal = ip.get_reader().expect("fbp_semantic : cannot get reader");
        let literal: lexical::Reader = literal.get_root().expect("fbp_semantic : not a literal");
        let literal = literal.which().expect("fbp_semantic : cannot which");
        match literal {
            lexical::End(_) => {
                break;
            },
            lexical::Token(t) => {
                let token = t.which().expect("cannot which token");
                if state == Error {
                    if let lexical::token::Break(()) = token {
                        if state != CompPortBind && state != IIPBind { state = Break; }
                    }
                    else if let lexical::token::Comp(_) = token {
                        state = Break;
                    }
                    else { continue; }
                }
                match token {
                    lexical::token::Bind(_) => {
                        state = match state {
                            CompPort => { CompPortBind },
                            IIP => { IIPBind },
                            _ => {
                                errors.push(format!("line {} : -> found, one of {} expected", line, get_expected(&state)));
                                Error
                            },
                        };
                    },
                    lexical::token::External(_) => {
                        state = match state {
                            CompPort => { CompPortExternal },
                            Port => { PortExternal },
                            _ => {
                                errors.push(format!("line {} : => found, one of {} expected", line, get_expected(&state)));
                                Error
                            },
                        };
                    },
                    lexical::token::Port(port) => {
                        stack.push(Literal::Port(port.get_name().unwrap().to_string(), port.get_selection().unwrap().to_string()));
                        state = match state {
                            Comp => { CompPort },
                            CompPortBind => { CompPortBindPort },
                            CompPortExternal => {
                                let in_p = stack.pop().unwrap();
                                let out_p = stack.pop().unwrap();
                                let out_c = stack.pop().unwrap();
                                {
                                    let (in_p_n, _) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(ref n, ref s) = out_c { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_out.push((out_c_n.clone(), out_p_n, out_p_s, in_p_n));
                                }
                                CompPortExternalPort },
                            Break => { Port },
                            PortExternal => { PortExternalPort },
                            IIPBind => { IIPBindPort },
                            _ => {
                                errors.push(format!("line {} : Port {}[{}] found, one of {} expected", line, port.get_name().unwrap(), port.get_selection().unwrap(), get_expected(&state)));
                                Error
                            },
                        };
                    },
                    lexical::token::Comp(comp) => {
                        if comp.get_sort().unwrap() != "" {
                            graph.nodes.push((comp.get_name().unwrap().to_string(), comp.get_sort().unwrap().to_string()));
                        }
                        stack.push(Literal::Comp(comp.get_name().unwrap().to_string(), comp.get_sort().unwrap().to_string()));
                        state = match state {
                            CompPortBindPort => {
                                let in_c = stack.pop().unwrap();
                                let in_p = stack.pop().unwrap();
                                let out_p = stack.pop().unwrap();
                                let out_c = stack.pop().unwrap();
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(n, s) = out_c { (n, s) } else { unreachable!() };
                                    graph.edges.push((out_c_n, out_p_n, out_p_s, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            },
                            PortExternalPort => {
                                let in_c = stack.pop().unwrap();
                                let in_p = stack.pop().unwrap();
                                let out_p = stack.pop().unwrap();
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_in.push((out_p_n, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            }
                            IIPBindPort => {
                                let in_c = stack.pop().unwrap();
                                let in_p = stack.pop().unwrap();
                                let iip = stack.pop().unwrap();
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let iip = if let Literal::IIP(iip) = iip { iip } else { unreachable!() };
                                    graph.iips.push((iip, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            }
                            Break => { Comp },
                            _ => {
                                errors.push(format!("line {} : Comp {}({}) found, one of {} expected", line, comp.get_name().unwrap(), comp.get_sort().unwrap(), get_expected(&state)));
                                Error
                            },
                        }
                    },
                    lexical::token::Iip(iip) => {
                        let iip = iip.expect("no iip");
                        stack.push(Literal::IIP(iip.to_string()));
                        state = match state {
                            Break => { IIP },
                            _ => {
                                errors.push(format!("line {} : IIP '{}' found, one of {} expected", line, iip, get_expected(&state)));
                                Error
                            },
                        };
                    },
                    lexical::token::Break(_) => {
                        line += 1;
                        state = match state {
                            CompPortBind => { state },
                            IIPBind => { state },
                            _ => {
                                stack.clear();
                                Break
                            },
                        };
                    }
                }
            },
            _ => { panic!("bad stream"); },
        }
    }
    if errors.len() > 0 {
        Err(errors)
    } else {
        Ok(graph)
    }
}

fn get_expected(state: &State) -> String {
    match *state {
        Break => { "[Component, Port, IIP, NewLine]".into() },
        Comp => { "[Port, NewLine]".into() },
        Port => { "[Component, ->, =>]".into() },
        IIP => { "[->]".into() },
        CompPort => { "[->, =>]".into() },
        CompPortBind => { "[Port]".into() },
        CompPortBindPort => { "[Component]".into() },
        CompPortExternal => { "[Port]".into() },
        CompPortExternalPort => { "[NewLine]".into() },
        PortExternal => { "[Port]".into() },
        PortExternalPort => { "[Component]".into() },
        IIPBind => { "[Port]".into() },
        IIPBindPort => { "[Component]".into() },
        Error => { unreachable!() }
    }
}

fn send_graph(comp: &fbp_semantic, path: &str, graph: &Graph) {
    let mut new_ip = capnp::message::Builder::new_default();
    {
        let mut ip = new_ip.init_root::<graph::Builder>();
        ip.set_path(path);
        {
            let mut nodes = ip.borrow().init_nodes(graph.nodes.len() as u32);
            let mut i = 0;
            for n in &graph.nodes {
                nodes.borrow().get(i).set_name(&n.0[..]);
                nodes.borrow().get(i).set_sort(&n.1[..]);
                i += 1;
            }
        }
        {
            let mut edges = ip.borrow().init_edges(graph.edges.len() as u32);
            let mut i = 0;
            for e in &graph.edges {
                edges.borrow().get(i).set_o_name(&e.0[..]);
                edges.borrow().get(i).set_o_port(&e.1[..]);
                edges.borrow().get(i).set_o_selection(&e.2[..]);
                edges.borrow().get(i).set_i_port(&e.3[..]);
                edges.borrow().get(i).set_i_selection(&e.4[..]);
                edges.borrow().get(i).set_i_name(&e.5[..]);
                i += 1;
            }
        }
        {
            let mut iips = ip.borrow().init_iips(graph.iips.len() as u32);
            let mut i = 0;
            for iip in &graph.iips {
                iips.borrow().get(i).set_iip(&iip.0[..]);
                iips.borrow().get(i).set_port(&iip.1[..]);
                iips.borrow().get(i).set_selection(&iip.2[..]);
                iips.borrow().get(i).set_comp(&iip.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = ip.borrow().init_external_inputs(graph.ext_in.len() as u32);
            let mut i = 0;
            for e in &graph.ext_in {
                ext.borrow().get(i).set_name(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_comp(&e.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = ip.borrow().init_external_outputs(graph.ext_out.len() as u32);
            let mut i = 0;
            for e in &graph.ext_out {
                ext.borrow().get(i).set_comp(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_name(&e.3[..]);
                i += 1;
            }
        }
    }
    let mut send_ip = comp.allocator.ip.build_empty();
    send_ip.write_builder(&new_ip).expect("fbp_lexical: cannot write");
    let _ = comp.ports.send("output".into(), send_ip);
}
<commit_msg>Problem : NewLine in semantic errors<commit_after>#[macro_use]
extern crate rustfbp;
use rustfbp::component::*;

extern crate capnp;

mod contract_capnp {
    include!("fbp_graph.rs");
    include!("fbp_lexical.rs");
    include!("fbp_semantic_error.rs");
}
use contract_capnp::graph;
use contract_capnp::lexical;
use contract_capnp::semantic_error;

#[derive(Debug)]
struct Graph {
    nodes: Vec<(String, String)>,
    edges: Vec<(String, String, String, String, String, String)>,
    iips: Vec<(String, String, String, String)>,
    ext_in: Vec<(String, String, String, String)>,
    ext_out: Vec<(String, String, String, String)>,
}

#[derive(PartialEq, Debug)]
enum State {
    Break, Comp, Port, IIP,
    CompPort, CompPortBind, CompPortBindPort,
    CompPortExternal, CompPortExternalPort,
    PortExternal, PortExternalPort,
    IIPBind, IIPBindPort,
    Error
}
use State::*;

enum Literal {
    Comp(String, String),
    Port(String, String),
    IIP(String),
    Bind, External,
}

component! {
    fbp_semantic,
    inputs(input: fbp_lexical),
    inputs_array(),
    outputs(output: fbp_graph, error: fbp_semantic_error),
    outputs_array(),
    option(),
    acc(),
    fn run(&mut self) {
        let mut ip = self.ports.recv("input".into()).expect("fbp_semantic : unable to receive");
        let literal = ip.get_reader().expect("fbp_semantic : cannot get reader");
        let literal: lexical::Reader = literal.get_root().expect("fbp_semantic : not a literal");
        let literal = literal.which().expect("fbp_semantic : cannot which");

        match literal {
            lexical::Start(path) => {
                match handle_stream(&self) {
                    Ok(graph) => { send_graph(&self, path.expect("not a path"), &graph) },
                    Err(errors) => {
                        let mut new_ip = capnp::message::Builder::new_default();
                        {
                            let mut ip = new_ip.init_root::<semantic_error::Builder>();
                            ip.set_path(path.unwrap());
                            {
                                let mut nodes = ip.init_parsing(errors.len() as u32);
                                let mut i = 0;
                                for n in &errors {
                                    nodes.borrow().set(i, &n[..]);
                                    i += 1;
                                }
                            }
                        }
                        let mut send_ip = self.allocator.ip.build_empty();
                        send_ip.write_builder(&new_ip).expect("fbp_lexical: cannot write");
                        let _ = self.ports.send("error".into(), send_ip);
                    },
                }
            }
            _ => { panic!("bad stream") },
        }
    }
}

fn handle_stream(comp: &fbp_semantic) -> std::result::Result<Graph, Vec<String>> {
    let mut state = Break;
    let mut stack: Vec<Literal> = vec![];
    let mut graph = Graph {
        nodes: vec![],
        edges: vec![],
        iips: vec![],
        ext_in: vec![],
        ext_out: vec![]
    };
    let mut errors: Vec<String> = vec![];
    let mut line: usize = 1;

    loop {

        let mut ip = comp.ports.recv("input".into()).expect("fbp_semantic : unable to receive");
        let literal = ip.get_reader().expect("fbp_semantic : cannot get reader");
        let literal: lexical::Reader = literal.get_root().expect("fbp_semantic : not a literal");
        let literal = literal.which().expect("fbp_semantic : cannot which");
        match literal {
            lexical::End(_) => {
                break;
            },
            lexical::Token(t) => {
                let token = t.which().expect("cannot which token");
                match token {
                    lexical::token::Bind(_) => {
                        state = match state {
                            CompPort => { CompPortBind },
                            Error => { Error },
                            IIP => { IIPBind },
                            _ => {
                                errors.push(format!("line {} : -> found, one of {} expected", line, get_expected(&state)));
                                Error
                            },
                        };
                    },
                    lexical::token::External(_) => {
                        state = match state {
                            CompPort => { CompPortExternal },
                            Port => { PortExternal },
                            Error => { Error },
                            _ => {
                                errors.push(format!("line {} : => found, one of {} expected", line, get_expected(&state)));
                                Error
                            },
                        };
                    },
                    lexical::token::Port(port) => {
                        stack.push(Literal::Port(port.get_name().unwrap().to_string(), port.get_selection().unwrap().to_string()));
                        state = match state {
                            Comp => { CompPort },
                            CompPortBind => { CompPortBindPort },
                            CompPortExternal => {
                                let in_p = stack.pop().unwrap();
                                let out_p = stack.pop().unwrap();
                                let out_c = stack.pop().unwrap();
                                {
                                    let (in_p_n, _) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(ref n, ref s) = out_c { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_out.push((out_c_n.clone(), out_p_n, out_p_s, in_p_n));
                                }
                                CompPortExternalPort },
                            Break => { Port },
                            PortExternal => { PortExternalPort },
                            Error => { Error },
                            IIPBind => { IIPBindPort },
                            _ => {
                                errors.push(format!("line {} : Port {}[{}] found, one of {} expected", line, port.get_name().unwrap(), port.get_selection().unwrap(), get_expected(&state)));
                                Error
                            },
                        };
                    },
                    lexical::token::Comp(comp) => {
                        if comp.get_sort().unwrap() != "" {
                            graph.nodes.push((comp.get_name().unwrap().to_string(), comp.get_sort().unwrap().to_string()));
                        }
                        stack.push(Literal::Comp(comp.get_name().unwrap().to_string(), comp.get_sort().unwrap().to_string()));
                        state = match state {
                            CompPortBindPort => {
                                let in_c = stack.pop().unwrap();
                                let in_p = stack.pop().unwrap();
                                let out_p = stack.pop().unwrap();
                                let out_c = stack.pop().unwrap();
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(n, s) = out_c { (n, s) } else { unreachable!() };
                                    graph.edges.push((out_c_n, out_p_n, out_p_s, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            },
                            PortExternalPort => {
                                let in_c = stack.pop().unwrap();
                                let in_p = stack.pop().unwrap();
                                let out_p = stack.pop().unwrap();
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_in.push((out_p_n, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            }
                            IIPBindPort => {
                                let in_c = stack.pop().unwrap();
                                let in_p = stack.pop().unwrap();
                                let iip = stack.pop().unwrap();
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let iip = if let Literal::IIP(iip) = iip { iip } else { unreachable!() };
                                    graph.iips.push((iip, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Comp
                            }
                            Break => { Comp },
                            Error => { stack = vec![stack.pop().unwrap()]; Comp },
                            _ => {
                                errors.push(format!("line {} : Comp {}({}) found, one of {} expected", line, comp.get_name().unwrap(), comp.get_sort().unwrap(), get_expected(&state)));
                                Error
                            },
                        }
                    },
                    lexical::token::Iip(iip) => {
                        let iip = iip.expect("no iip");
                        stack.push(Literal::IIP(iip.to_string()));
                        state = match state {
                            Error => { IIP },
                            Break => { IIP },
                            _ => {
                                errors.push(format!("line {} : IIP '{}' found, one of {} expected", line, iip, get_expected(&state)));
                                Error
                            },
                        };
                    },
                    lexical::token::Break(_) => {
                        line += 1;
                        state = match state {
                            CompPortBind => { state },
                            IIPBind => { state },
                            Comp => { stack.clear(); Break },
                            CompPortExternalPort => { stack.clear(); Break },
                            Break => { Break },
                            Error => { Error },
                            _ => {
                                errors.push(format!("line {} : NewLine found, one of {} expected", line, get_expected(&state)));
                                Error
                            },
                        };
                    }
                }
            },
            _ => { panic!("bad stream"); },
        }
    }
    if errors.len() > 0 {
        Err(errors)
    } else {
        Ok(graph)
    }
}

fn get_expected(state: &State) -> String {
    match *state {
        Break => { "[Component, Port, IIP, NewLine]".into() },
        Comp => { "[Port, NewLine]".into() },
        Port => { "[Component, ->, =>]".into() },
        IIP => { "[->]".into() },
        CompPort => { "[->, =>]".into() },
        CompPortBind => { "[Port]".into() },
        CompPortBindPort => { "[Component]".into() },
        CompPortExternal => { "[Port]".into() },
        CompPortExternalPort => { "[NewLine]".into() },
        PortExternal => { "[Port]".into() },
        PortExternalPort => { "[Component]".into() },
        IIPBind => { "[Port]".into() },
        IIPBindPort => { "[Component]".into() },
        Error => { unreachable!() }
    }
}

fn send_graph(comp: &fbp_semantic, path: &str, graph: &Graph) {
    let mut new_ip = capnp::message::Builder::new_default();
    {
        let mut ip = new_ip.init_root::<graph::Builder>();
        ip.set_path(path);
        {
            let mut nodes = ip.borrow().init_nodes(graph.nodes.len() as u32);
            let mut i = 0;
            for n in &graph.nodes {
                nodes.borrow().get(i).set_name(&n.0[..]);
                nodes.borrow().get(i).set_sort(&n.1[..]);
                i += 1;
            }
        }
        {
            let mut edges = ip.borrow().init_edges(graph.edges.len() as u32);
            let mut i = 0;
            for e in &graph.edges {
                edges.borrow().get(i).set_o_name(&e.0[..]);
                edges.borrow().get(i).set_o_port(&e.1[..]);
                edges.borrow().get(i).set_o_selection(&e.2[..]);
                edges.borrow().get(i).set_i_port(&e.3[..]);
                edges.borrow().get(i).set_i_selection(&e.4[..]);
                edges.borrow().get(i).set_i_name(&e.5[..]);
                i += 1;
            }
        }
        {
            let mut iips = ip.borrow().init_iips(graph.iips.len() as u32);
            let mut i = 0;
            for iip in &graph.iips {
                iips.borrow().get(i).set_iip(&iip.0[..]);
                iips.borrow().get(i).set_port(&iip.1[..]);
                iips.borrow().get(i).set_selection(&iip.2[..]);
                iips.borrow().get(i).set_comp(&iip.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = ip.borrow().init_external_inputs(graph.ext_in.len() as u32);
            let mut i = 0;
            for e in &graph.ext_in {
                ext.borrow().get(i).set_name(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_comp(&e.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = ip.borrow().init_external_outputs(graph.ext_out.len() as u32);
            let mut i = 0;
            for e in &graph.ext_out {
                ext.borrow().get(i).set_comp(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_name(&e.3[..]);
                i += 1;
            }
        }
    }
    let mut send_ip = comp.allocator.ip.build_empty();
    send_ip.write_builder(&new_ip).expect("fbp_lexical: cannot write");
    let _ = comp.ports.send("output".into(), send_ip);
}
<|endoftext|>


<commit_before>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::collections::HashSet;
use std::hash::Hash;

use codec::prelude::NumberDecoder;
use tidb_query_codegen::rpn_fn;
use tidb_query_datatype::EvalType;
use tipb::{Expr, ExprType};

use crate::codec::data_type::*;
use crate::codec::mysql::{Decimal, MAX_FSP};
use crate::{Error, Result};

pub trait Extract: std::marker::Sized {
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self>;
}

#[inline]
fn type_error(eval_type: EvalType, expr_type: ExprType) -> Error {
    return other_err!(
        "Unexpected ExprType {:?} and EvalType {:?}",
        expr_type,
        eval_type
    );
}

impl Extract for Int {
    #[inline]
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self> {
        if expr_tp == ExprType::Int64 {
            let value = val
                .as_slice()
                .read_i64()
                .map_err(|_| other_err!("Unable to decode int64 from the request"))?;
            Ok(value)
        } else if expr_tp == ExprType::Uint64 {
            let value = val
                .as_slice()
                .read_u64()
                .map_err(|_| other_err!("Unable to decode uint64 from the request"))?;
            Ok(value as i64)
        } else {
            Err(type_error(Int::EVAL_TYPE, expr_tp))
        }
    }
}

impl Extract for Real {
    #[inline]
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self> {
        if expr_tp != ExprType::Float32 && expr_tp != ExprType::Float64 {
            return Err(type_error(Real::EVAL_TYPE, expr_tp));
        }
        let value = val
            .as_slice()
            .read_f64()
            .map_err(|_| other_err!("Unable to decode float from the request"))?;
        Real::new(value).map_err(|_| other_err!("Unable to convert float to real"))
    }
}

impl Extract for Bytes {
    #[inline]
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self> {
        if expr_tp != ExprType::Bytes && expr_tp != ExprType::String {
            return Err(type_error(Bytes::EVAL_TYPE, expr_tp));
        }
        Ok(val)
    }
}

impl Extract for Decimal {
    #[inline]
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self> {
        if expr_tp != ExprType::MysqlDecimal {
            return Err(type_error(Decimal::EVAL_TYPE, expr_tp));
        }
        use crate::codec::mysql::DecimalDecoder;
        let value = val
            .as_slice()
            .read_decimal()
            .map_err(|_| other_err!("Unable to decode decimal from the request"))?;
        Ok(value)
    }
}

impl Extract for Duration {
    #[inline]
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self> {
        if expr_tp != ExprType::MysqlDuration {
            return Err(type_error(Duration::EVAL_TYPE, expr_tp));
        }
        let n = val
            .as_slice()
            .read_i64()
            .map_err(|_| other_err!("Unable to decode duration from the request"))?;
        let value = Duration::from_nanos(n, MAX_FSP)
            .map_err(|_| other_err!("Unable to decode duration from the request"))?;
        Ok(value)
    }
}

pub trait InByHash: Evaluable + Hash + Eq {}
pub trait InByCompare: Evaluable + Eq {}

impl InByHash for Int {}
impl InByHash for Real {}
impl InByHash for Bytes {}
impl InByHash for Decimal {}
impl InByHash for Duration {}

impl InByCompare for Int {}
impl InByCompare for Real {}
impl InByCompare for Bytes {}
impl InByCompare for Decimal {}
impl InByCompare for Duration {}
// DateTime requires TZInfo in context, and we cannot acquire it during metadata_ctor.
// TODO: implement InByHash for DateTime.
impl InByCompare for DateTime {}
// Implement Hash for Json is impossible, due to equality of Json depends on an epsilon.
impl InByCompare for Json {}

#[derive(Debug)]
pub struct CompareInMeta<T: Eq + Hash> {
    lookup_set: HashSet<T>,
    has_null: bool,
}

#[rpn_fn(varg, capture = [metadata], min_args = 1, metadata_ctor = init_compare_in_data::<T>)]
#[inline]
pub fn compare_in_by_hash<T: InByHash + Extract>(
    metadata: &CompareInMeta<T>,
    args: &[&Option<T>],
) -> Result<Option<Int>> {
    assert!(!args.is_empty());
    let base_val = args[0];
    match base_val {
        None => Ok(None),
        Some(base_val) => {
            if metadata.lookup_set.contains(base_val) {
                return Ok(Some(1));
            }
            let mut default_ret = if metadata.has_null { None } else { Some(0) };
            for arg in &args[1..] {
                match arg {
                    None => {
                        default_ret = None;
                    }
                    Some(v) => {
                        if v == base_val {
                            return Ok(Some(1));
                        }
                    }
                }
            }
            Ok(default_ret)
        }
    }
}

fn init_compare_in_data<T: InByHash + Extract>(expr: &mut Expr) -> Result<CompareInMeta<T>> {
    let mut lookup_set = HashSet::new();
    let mut has_null = false;
    let children = expr.mut_children();
    assert!(!children.is_empty());

    let n = children.len();
    let mut tail_index = n - 1;
    // try to evaluate and remove all constant nodes except args[0].
    for current_index in (n - 1)..0 {
        let tree_node = &mut children[current_index];
        let mut is_constant = true;
        match tree_node.get_tp() {
            ExprType::ScalarFunc | ExprType::ColumnRef => {
                is_constant = false;
            }
            ExprType::Null => {
                has_null = true;
            }
            expr_type => {
                let val = T::extract(expr_type, tree_node.take_val())?;
                lookup_set.insert(val);
            }
        }
        if is_constant {
            children.as_mut_slice().swap(current_index, tail_index);
            tail_index -= 1;
        }
    }
    children.truncate(tail_index + 1);

    Ok(CompareInMeta {
        lookup_set,
        has_null,
    })
}

#[rpn_fn(varg, min_args = 1)]
#[inline]
pub fn compare_in_by_compare<T: InByCompare>(args: &[&Option<T>]) -> Result<Option<Int>> {
    assert!(!args.is_empty());
    let base_val = args[0];
    match base_val {
        None => Ok(None),
        Some(base_val) => {
            let mut default_ret = Some(0);
            for arg in &args[1..] {
                match arg {
                    None => {
                        default_ret = None;
                    }
                    Some(v) => {
                        if v == base_val {
                            return Ok(Some(1));
                        }
                    }
                }
            }
            Ok(default_ret)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::map_expr_node_to_rpn_func;
    use super::*;

    use test::{black_box, Bencher};
    use tidb_query_datatype::FieldTypeTp;
    use tipb::ScalarFuncSig;
    use tipb_helper::ExprDefBuilder;

    use crate::codec::batch::{LazyBatchColumn, LazyBatchColumnVec};
    use crate::expr::EvalContext;
    use crate::rpn_expr::types::RpnFnMeta;
    use crate::rpn_expr::RpnExpressionBuilder;

    #[test]
    fn test_in() {
        // mapper to test compare_in_by_compare.
        fn by_compare_mapper(expr: &Expr) -> Result<RpnFnMeta> {
            match expr.get_sig() {
                ScalarFuncSig::InInt => Ok(compare_in_by_compare_fn_meta::<Int>()),
                _ => map_expr_node_to_rpn_func(expr),
            }
        }

        fn test_with_mapper<F>(mapper: F)
        where
            F: Fn(&Expr) -> Result<RpnFnMeta> + Copy,
        {
            let cases = vec![
                (vec![Some(1)], Some(0)),
                (vec![Some(1), Some(2)], Some(0)),
                (vec![Some(1), Some(2), Some(1)], Some(1)),
                (vec![Some(1), Some(2), None], None),
                (vec![Some(1), Some(2), None, Some(1)], Some(1)),
                (vec![None, Some(2), Some(1)], None),
            ];
            for (args, expected) in cases {
                let mut builder =
                    ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong);
                for arg in args {
                    builder = builder.push_child(match arg {
                        Some(v) => ExprDefBuilder::constant_int(v),
                        None => ExprDefBuilder::constant_null(FieldTypeTp::LongLong),
                    });
                }
                let node = builder.build();
                let exp =
                    RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(node, mapper, 1)
                        .unwrap();
                let mut ctx = EvalContext::default();
                let schema = &[FieldTypeTp::LongLong.into()];
                let mut columns = LazyBatchColumnVec::empty();
                let result = exp.eval(&mut ctx, schema, &mut columns, &[], 1);
                let val = result.unwrap();
                assert!(val.is_vector());
                assert_eq!(
                    val.vector_value().unwrap().as_ref().as_int_slice(),
                    &[expected]
                );
            }
        }

        test_with_mapper(map_expr_node_to_rpn_func);
        test_with_mapper(by_compare_mapper);
    }

    #[test]
    fn test_in_complex() {
        let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
            .push_child(ExprDefBuilder::constant_int(11))
            .push_child(ExprDefBuilder::constant_int(22))
            .push_child(
                ExprDefBuilder::scalar_func(ScalarFuncSig::PlusInt, FieldTypeTp::LongLong)
                    .push_child(ExprDefBuilder::constant_int(6))
                    .push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong)),
            )
            .push_child(ExprDefBuilder::column_ref(1, FieldTypeTp::LongLong))
            .build();
        let exp = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
            node,
            map_expr_node_to_rpn_func,
            2,
        )
        .unwrap();
        let mut ctx = EvalContext::default();
        let schema = &[FieldTypeTp::LongLong.into(), FieldTypeTp::LongLong.into()];
        let mut columns = LazyBatchColumnVec::from(vec![
            {
                let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(3, EvalType::Int);
                col.mut_decoded().push_int(Some(5)); // row 1, 11 in [(5 + 6), ...]
                col.mut_decoded().push_int(Some(1)); // row 0
                col.mut_decoded().push_int(Some(1)); // row 2
                col
            },
            {
                let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(3, EvalType::Int);
                col.mut_decoded().push_int(Some(8)); // row 1
                col.mut_decoded().push_int(Some(11)); // row 0, 11 in [11, ...]
                col.mut_decoded().push_int(Some(1)); // row 2
                col
            },
        ]);
        let result = exp.eval(&mut ctx, schema, &mut columns, &[1, 0, 2], 3);
        let val = result.unwrap();
        assert!(val.is_vector());
        assert_eq!(
            val.vector_value().unwrap().as_ref().as_int_slice(),
            &[Some(1), Some(1), Some(0)],
        );
    }

    #[bench]
    fn bench_compare_in(b: &mut Bencher) {
        let mut builder = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
            .push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong));
        for i in 0..1024 {
            builder = builder.push_child(ExprDefBuilder::constant_int(i));
        }
        let node = builder.build();

        profiler::start("./bench_compare_in.profile");
        let exp = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
            node,
            map_expr_node_to_rpn_func,
            1,
        )
        .unwrap();

        let mut ctx = EvalContext::default();
        let schema = &[FieldTypeTp::LongLong.into()];
        let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(1024, EvalType::Int);
        for i in 0..1024 {
            col.mut_decoded().push_int(Some(i));
        }
        let mut columns = LazyBatchColumnVec::from(vec![col]);
        let logical_rows: &[usize] = &(0..1024).collect::<Vec<usize>>();
        profiler::start("./bench_compare_in.profile");
        b.iter(|| {
            let result = black_box(&exp).eval(
                black_box(&mut ctx),
                black_box(schema),
                black_box(&mut columns),
                black_box(&logical_rows),
                black_box(1024),
            );
            assert!(result.is_ok());
        });
        profiler::stop();
    }
}
<commit_msg>Fix the bug that IN() optimization is not taking effect (#6093)<commit_after>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::collections::HashSet;
use std::hash::Hash;

use codec::prelude::NumberDecoder;
use tidb_query_codegen::rpn_fn;
use tidb_query_datatype::EvalType;
use tipb::{Expr, ExprType};

use crate::codec::data_type::*;
use crate::codec::mysql::{Decimal, MAX_FSP};
use crate::{Error, Result};

pub trait Extract: std::marker::Sized {
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self>;
}

#[inline]
fn type_error(eval_type: EvalType, expr_type: ExprType) -> Error {
    return other_err!(
        "Unexpected ExprType {:?} and EvalType {:?}",
        expr_type,
        eval_type
    );
}

impl Extract for Int {
    #[inline]
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self> {
        if expr_tp == ExprType::Int64 {
            let value = val
                .as_slice()
                .read_i64()
                .map_err(|_| other_err!("Unable to decode int64 from the request"))?;
            Ok(value)
        } else if expr_tp == ExprType::Uint64 {
            let value = val
                .as_slice()
                .read_u64()
                .map_err(|_| other_err!("Unable to decode uint64 from the request"))?;
            Ok(value as i64)
        } else {
            Err(type_error(Int::EVAL_TYPE, expr_tp))
        }
    }
}

impl Extract for Real {
    #[inline]
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self> {
        if expr_tp != ExprType::Float32 && expr_tp != ExprType::Float64 {
            return Err(type_error(Real::EVAL_TYPE, expr_tp));
        }
        let value = val
            .as_slice()
            .read_f64()
            .map_err(|_| other_err!("Unable to decode float from the request"))?;
        Real::new(value).map_err(|_| other_err!("Unable to convert float to real"))
    }
}

impl Extract for Bytes {
    #[inline]
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self> {
        if expr_tp != ExprType::Bytes && expr_tp != ExprType::String {
            return Err(type_error(Bytes::EVAL_TYPE, expr_tp));
        }
        Ok(val)
    }
}

impl Extract for Decimal {
    #[inline]
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self> {
        if expr_tp != ExprType::MysqlDecimal {
            return Err(type_error(Decimal::EVAL_TYPE, expr_tp));
        }
        use crate::codec::mysql::DecimalDecoder;
        let value = val
            .as_slice()
            .read_decimal()
            .map_err(|_| other_err!("Unable to decode decimal from the request"))?;
        Ok(value)
    }
}

impl Extract for Duration {
    #[inline]
    fn extract(expr_tp: ExprType, val: Vec<u8>) -> Result<Self> {
        if expr_tp != ExprType::MysqlDuration {
            return Err(type_error(Duration::EVAL_TYPE, expr_tp));
        }
        let n = val
            .as_slice()
            .read_i64()
            .map_err(|_| other_err!("Unable to decode duration from the request"))?;
        let value = Duration::from_nanos(n, MAX_FSP)
            .map_err(|_| other_err!("Unable to decode duration from the request"))?;
        Ok(value)
    }
}

pub trait InByHash: Evaluable + Hash + Eq {}
pub trait InByCompare: Evaluable + Eq {}

impl InByHash for Int {}
impl InByHash for Real {}
impl InByHash for Bytes {}
impl InByHash for Decimal {}
impl InByHash for Duration {}

impl InByCompare for Int {}
impl InByCompare for Real {}
impl InByCompare for Bytes {}
impl InByCompare for Decimal {}
impl InByCompare for Duration {}
// DateTime requires TZInfo in context, and we cannot acquire it during metadata_ctor.
// TODO: implement InByHash for DateTime.
impl InByCompare for DateTime {}
// Implement Hash for Json is impossible, due to equality of Json depends on an epsilon.
impl InByCompare for Json {}

#[derive(Debug)]
pub struct CompareInMeta<T: Eq + Hash> {
    lookup_set: HashSet<T>,
    has_null: bool,
}

#[rpn_fn(varg, capture = [metadata], min_args = 1, metadata_ctor = init_compare_in_data::<T>)]
#[inline]
pub fn compare_in_by_hash<T: InByHash + Extract>(
    metadata: &CompareInMeta<T>,
    args: &[&Option<T>],
) -> Result<Option<Int>> {
    assert!(!args.is_empty());
    let base_val = args[0];
    match base_val {
        None => Ok(None),
        Some(base_val) => {
            if metadata.lookup_set.contains(base_val) {
                return Ok(Some(1));
            }
            let mut default_ret = if metadata.has_null { None } else { Some(0) };
            for arg in &args[1..] {
                match arg {
                    None => {
                        default_ret = None;
                    }
                    Some(v) => {
                        if v == base_val {
                            return Ok(Some(1));
                        }
                    }
                }
            }
            Ok(default_ret)
        }
    }
}

fn init_compare_in_data<T: InByHash + Extract>(expr: &mut Expr) -> Result<CompareInMeta<T>> {
    let mut lookup_set = HashSet::new();
    let mut has_null = false;
    let children = expr.mut_children();
    assert!(!children.is_empty());

    let n = children.len();
    let mut tail_index = n - 1;
    // try to evaluate and remove all constant nodes except args[0].
    for i in (1..n).rev() {
        let tree_node = &mut children[i];
        let mut is_constant = true;
        match tree_node.get_tp() {
            ExprType::ScalarFunc | ExprType::ColumnRef => {
                is_constant = false;
            }
            ExprType::Null => {
                has_null = true;
            }
            expr_type => {
                let val = T::extract(expr_type, tree_node.take_val())?;
                lookup_set.insert(val);
            }
        }
        if is_constant {
            children.as_mut_slice().swap(i, tail_index);
            tail_index -= 1;
        }
    }
    children.truncate(tail_index + 1);

    Ok(CompareInMeta {
        lookup_set,
        has_null,
    })
}

#[rpn_fn(varg, min_args = 1)]
#[inline]
pub fn compare_in_by_compare<T: InByCompare>(args: &[&Option<T>]) -> Result<Option<Int>> {
    assert!(!args.is_empty());
    let base_val = args[0];
    match base_val {
        None => Ok(None),
        Some(base_val) => {
            let mut default_ret = Some(0);
            for arg in &args[1..] {
                match arg {
                    None => {
                        default_ret = None;
                    }
                    Some(v) => {
                        if v == base_val {
                            return Ok(Some(1));
                        }
                    }
                }
            }
            Ok(default_ret)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::map_expr_node_to_rpn_func;
    use super::*;

    use test::{black_box, Bencher};
    use tidb_query_datatype::FieldTypeTp;
    use tipb::ScalarFuncSig;
    use tipb_helper::ExprDefBuilder;

    use crate::codec::batch::{LazyBatchColumn, LazyBatchColumnVec};
    use crate::expr::EvalContext;
    use crate::rpn_expr::types::RpnFnMeta;
    use crate::rpn_expr::RpnExpressionBuilder;

    #[test]
    fn test_in() {
        // mapper to test compare_in_by_compare.
        fn by_compare_mapper(expr: &Expr) -> Result<RpnFnMeta> {
            match expr.get_sig() {
                ScalarFuncSig::InInt => Ok(compare_in_by_compare_fn_meta::<Int>()),
                _ => map_expr_node_to_rpn_func(expr),
            }
        }

        fn test_with_mapper<F>(mapper: F)
        where
            F: Fn(&Expr) -> Result<RpnFnMeta> + Copy,
        {
            let cases = vec![
                (vec![Some(1)], Some(0)),
                (vec![Some(1), Some(2)], Some(0)),
                (vec![Some(1), Some(2), Some(1)], Some(1)),
                (vec![Some(1), Some(2), None], None),
                (vec![Some(1), Some(2), None, Some(1)], Some(1)),
                (vec![None, Some(2), Some(1)], None),
            ];
            for (args, expected) in cases {
                let mut builder =
                    ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong);
                for arg in args {
                    builder = builder.push_child(match arg {
                        Some(v) => ExprDefBuilder::constant_int(v),
                        None => ExprDefBuilder::constant_null(FieldTypeTp::LongLong),
                    });
                }
                let node = builder.build();
                let exp =
                    RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(node, mapper, 1)
                        .unwrap();
                let mut ctx = EvalContext::default();
                let schema = &[FieldTypeTp::LongLong.into()];
                let mut columns = LazyBatchColumnVec::empty();
                let result = exp.eval(&mut ctx, schema, &mut columns, &[], 1);
                let val = result.unwrap();
                assert!(val.is_vector());
                assert_eq!(
                    val.vector_value().unwrap().as_ref().as_int_slice(),
                    &[expected]
                );
            }
        }

        test_with_mapper(map_expr_node_to_rpn_func);
        test_with_mapper(by_compare_mapper);
    }

    #[test]
    fn test_in_complex() {
        let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
            .push_child(ExprDefBuilder::constant_int(11))
            .push_child(ExprDefBuilder::constant_int(22))
            .push_child(
                ExprDefBuilder::scalar_func(ScalarFuncSig::PlusInt, FieldTypeTp::LongLong)
                    .push_child(ExprDefBuilder::constant_int(6))
                    .push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong)),
            )
            .push_child(ExprDefBuilder::column_ref(1, FieldTypeTp::LongLong))
            .build();
        let exp = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
            node,
            map_expr_node_to_rpn_func,
            2,
        )
        .unwrap();
        let mut ctx = EvalContext::default();
        let schema = &[FieldTypeTp::LongLong.into(), FieldTypeTp::LongLong.into()];
        let mut columns = LazyBatchColumnVec::from(vec![
            {
                let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(3, EvalType::Int);
                col.mut_decoded().push_int(Some(5)); // row 1, 11 in [(5 + 6), ...]
                col.mut_decoded().push_int(Some(1)); // row 0
                col.mut_decoded().push_int(Some(1)); // row 2
                col
            },
            {
                let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(3, EvalType::Int);
                col.mut_decoded().push_int(Some(8)); // row 1
                col.mut_decoded().push_int(Some(11)); // row 0, 11 in [11, ...]
                col.mut_decoded().push_int(Some(1)); // row 2
                col
            },
        ]);
        let result = exp.eval(&mut ctx, schema, &mut columns, &[1, 0, 2], 3);
        let val = result.unwrap();
        assert!(val.is_vector());
        assert_eq!(
            val.vector_value().unwrap().as_ref().as_int_slice(),
            &[Some(1), Some(1), Some(0)],
        );
    }

    #[bench]
    fn bench_compare_in(b: &mut Bencher) {
        let mut builder = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
            .push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong));
        for i in 0..1024 {
            builder = builder.push_child(ExprDefBuilder::constant_int(i));
        }
        let node = builder.build();

        profiler::start("./bench_compare_in.profile");
        let exp = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
            node,
            map_expr_node_to_rpn_func,
            1,
        )
        .unwrap();

        let mut ctx = EvalContext::default();
        let schema = &[FieldTypeTp::LongLong.into()];
        let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(1024, EvalType::Int);
        for i in 0..1024 {
            col.mut_decoded().push_int(Some(i));
        }
        let mut columns = LazyBatchColumnVec::from(vec![col]);
        let logical_rows: &[usize] = &(0..1024).collect::<Vec<usize>>();
        profiler::start("./bench_compare_in.profile");
        b.iter(|| {
            let result = black_box(&exp).eval(
                black_box(&mut ctx),
                black_box(schema),
                black_box(&mut columns),
                black_box(&logical_rows),
                black_box(1024),
            );
            assert!(result.is_ok());
        });
        profiler::stop();
    }
}
<|endoftext|>