<commit_before>use regex::Regex;

static IGNORE_WHITESPACE: &'static str = r"\s*";
static ANY_WHITESPACE: &'static str = r"\s+";
static FIELD_TYPE: &'static str = r"([a-zA-Z0-9_/]+)";
static FIELD_NAME: &'static str = r"([a-zA-Z][a-zA-Z0-9_]*)";
static EMPTY_BRACKETS: &'static str = r"\[\s*\]";
static NUMBER_BRACKETS: &'static str = r"\[\s*([0-9]+)\s*\]";

fn match_field(data: &str) -> Option<FieldLine> {
    lazy_static! {
        static ref MATCHER: String = format!("^{}{}{}$", FIELD_TYPE, ANY_WHITESPACE, FIELD_NAME);
        static ref RE: Regex = Regex::new(&MATCHER).unwrap();
    }
    let captures = match RE.captures(data) {
        Some(v) => v,
        None => return None,
    };
    Some(FieldLine {
        field_type: captures.get(1).unwrap().as_str().into(),
        field_name: captures.get(2).unwrap().as_str().into(),
    })
}

fn match_vector_field(data: &str) -> Option<FieldLine> {
    lazy_static! {
        static ref MATCHER: String = format!(
            "^{}{}{}{}{}$", FIELD_TYPE, IGNORE_WHITESPACE, EMPTY_BRACKETS, ANY_WHITESPACE,
            FIELD_NAME);
        static ref RE: Regex = Regex::new(&MATCHER).unwrap();
    }
    let captures = match RE.captures(data) {
        Some(v) => v,
        None => return None,
    };
    Some(FieldLine {
        field_type: captures.get(1).unwrap().as_str().into(),
        field_name: captures.get(2).unwrap().as_str().into(),
    })
}

fn match_array_field(data: &str) -> Option<(FieldLine, usize)> {
    lazy_static! {
        static ref MATCHER: String = format!(
            "^{}{}{}{}{}$", FIELD_TYPE, IGNORE_WHITESPACE, NUMBER_BRACKETS, ANY_WHITESPACE,
            FIELD_NAME);
        static ref RE: Regex = Regex::new(&MATCHER).unwrap();
    }
    let captures = match RE.captures(data) {
        Some(v) => v,
        None => return None,
    };
    Some((FieldLine {
              field_type: captures.get(1).unwrap().as_str().into(),
              field_name: captures.get(3).unwrap().as_str().into(),
          },
          captures.get(2).unwrap().as_str().parse().unwrap()))
}

fn match_const_string(data: &str) -> Option<(FieldLine, String)> {
    lazy_static! {
        static ref MATCHER: String = format!(
            r"^(string){}{}{}={}(.*)$", ANY_WHITESPACE, FIELD_NAME, IGNORE_WHITESPACE,
            IGNORE_WHITESPACE);
        static ref RE: Regex = Regex::new(&MATCHER).unwrap();
    }
    let captures = match RE.captures(data) {
        Some(v) => v,
        None => return None,
    };
    Some((FieldLine {
              field_type: captures.get(1).unwrap().as_str().into(),
              field_name: captures.get(2).unwrap().as_str().into(),
          },
          captures.get(3).unwrap().as_str().into()))
}

fn match_const_numeric(data: &str) -> Option<(FieldLine, String)> {
    lazy_static! {
        static ref MATCHER: String = format!(
            r"^{}{}{}{}={}(-?[0-9]+)$", FIELD_TYPE, ANY_WHITESPACE, FIELD_NAME,
            IGNORE_WHITESPACE, IGNORE_WHITESPACE);
        static ref RE: Regex = Regex::new(&MATCHER).unwrap();
    }
    let captures = match RE.captures(data) {
        Some(v) => v,
        None => return None,
    };
    Some((FieldLine {
              field_type: captures.get(1).unwrap().as_str().into(),
              field_name: captures.get(2).unwrap().as_str().into(),
          },
          captures.get(3).unwrap().as_str().into()))
}

fn match_line(data: &str) -> Result<Option<FieldInfo>, ()> {
    if let Some((info, data)) = match_const_string(data.trim()) {
        return Ok(Some(FieldInfo {
            datatype: info.field_type,
            name: info.field_name,
            case: FieldCase::Const(data),
        }));
    }
    let data = data.splitn(2, '#').next().unwrap().trim();
    if data == "" {
        return Ok(None);
    }
    if let Some(info) = match_field(data) {
        return Ok(Some(FieldInfo {
            datatype: info.field_type,
            name: info.field_name,
            case: FieldCase::Unit,
        }));
    }
    if let Some(info) = match_vector_field(data) {
        return Ok(Some(FieldInfo {
            datatype: info.field_type,
            name: info.field_name,
            case: FieldCase::Vector,
        }));
    }
    if let Some((info, count)) = match_array_field(data) {
        return Ok(Some(FieldInfo {
            datatype: info.field_type,
            name: info.field_name,
            case: FieldCase::Array(count),
        }));
    }
    if let Some((info, data)) = match_const_numeric(data) {
        return Ok(Some(FieldInfo {
            datatype: info.field_type,
            name: info.field_name,
            case: FieldCase::Const(data),
        }));
    }
    Err(())
}

#[inline]
fn match_lines(data: &str) -> Result<Vec<FieldInfo>, ()> {
    let lines: Result<Vec<Option<_>>, ()> = data.split('\n').map(match_line).collect();
    Ok(lines?.into_iter().filter_map(|v| v).collect())
}

#[derive(Debug,PartialEq)]
struct FieldLine {
    field_type: String,
    field_name: String,
}

#[derive(Debug,PartialEq)]
enum FieldCase {
    Unit,
    Vector,
    Array(usize),
    Const(String),
}

#[derive(Clone,Debug,PartialEq)]
enum MemberCase {
    Unit,
    Vector,
    Array(usize),
}

impl<'a> From<&'a FieldCase> for MemberCase {
    fn from(case: &'a FieldCase) -> MemberCase {
        match *case {
            FieldCase::Unit => MemberCase::Unit,
            FieldCase::Vector => MemberCase::Vector,
            FieldCase::Array(v) => MemberCase::Array(v),
            _ => panic!("Conversion not supported"),
        }
    }
}

#[derive(Debug,PartialEq)]
struct FieldInfo {
    datatype: String,
    name: String,
    case: FieldCase,
}

#[derive(Debug,PartialEq)]
enum DataType {
    Bool,
    I8,
    I16,
    I32,
    I64,
    U8,
    U16,
    U32,
    U64,
    F32,
    F64,
    String,
    Time,
    Duration,
    LocalStruct(String),
    RemoteStruct(String, String),
}

#[derive(Debug,PartialEq)]
struct MemberInfo {
    datatype: DataType,
    name: String,
    case: MemberCase,
}

fn parse_members(fields: &[FieldInfo]) -> Result<Vec<MemberInfo>, ()> {
    fields.iter()
        .map(|v| {
            Ok(MemberInfo {
                datatype: parse_datatype(&v.datatype).ok_or(())?,
                name: v.name.clone(),
                case: MemberCase::from(&v.case),
            })
        })
        .collect()
}

fn parse_datatype(datatype: &str) -> Option<DataType> {
    match datatype {
        "bool" => Some(DataType::Bool),
        "int8" => Some(DataType::I8),
        "int16" => Some(DataType::I16),
        "int32" => Some(DataType::I32),
        "int64" => Some(DataType::I64),
        "uint8" => Some(DataType::U8),
        "uint16" => Some(DataType::U16),
        "uint32" => Some(DataType::U32),
        "uint64" => Some(DataType::U64),
        "float32" => Some(DataType::F32),
        "float64" => Some(DataType::F64),
        "string" => Some(DataType::String),
        "time" => Some(DataType::Time),
        "duration" => Some(DataType::Duration),
        _ => {
            let parts = datatype.split('/').collect::<Vec<_>>();
            if parts.iter().any(|v| v.len() == 0) {
                return None;
            }
            match parts.len() {
                2 => Some(DataType::RemoteStruct(parts[0].to_owned(), parts[1].to_owned())),
                1 => Some(DataType::LocalStruct(parts[0].to_owned())),
                _ => None,
            }
        }
    }
}

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

    #[test]
    fn match_field_matches_legal_field() {
        assert_eq!(FieldLine {
                       field_type: "geom_msgs/Twist".into(),
                       field_name: "myname".into(),
                   },
                   match_field("geom_msgs/Twist   myname").unwrap());
    }

    #[test]
    fn match_vector_field_matches_legal_field() {
        assert_eq!(FieldLine {
                       field_type: "geom_msgs/Twist".into(),
                       field_name: "myname".into(),
                   },
                   match_vector_field("geom_msgs/Twist [  ]   myname").unwrap());
    }

    #[test]
    fn match_array_field_matches_legal_field() {
        assert_eq!((FieldLine {
                        field_type: "geom_msgs/Twist".into(),
                        field_name: "myname".into(),
                    },
                    127),
                   match_array_field("geom_msgs/Twist   [   127 ]   myname").unwrap());
    }

    #[test]
    fn match_const_string_matches_legal_field() {
        assert_eq!((FieldLine {
                        field_type: "string".into(),
                        field_name: "myname".into(),
                    },
                    "this is # data".into()),
                   match_const_string("string   myname  =  this is # data").unwrap());
    }

    #[test]
    fn match_const_numeric_matches_legal_field() {
        assert_eq!((FieldLine {
                        field_type: "mytype".into(),
                        field_name: "myname".into(),
                    },
                    "-444".into()),
                   match_const_numeric("mytype   myname  =  -444").unwrap());
    }

    #[test]
    fn match_line_works_on_legal_data() {
        assert!(match_line("#just a comment").unwrap().is_none());
        assert!(match_line("#  YOLO !   ").unwrap().is_none());
        assert!(match_line("      ").unwrap().is_none());

        assert_eq!(FieldInfo {
                       datatype: "geom_msgs/Twist".into(),
                       name: "myname".into(),
                       case: FieldCase::Unit,
                   },
                   match_line("  geom_msgs/Twist   myname    # this clearly should succeed")
                       .unwrap()
                       .unwrap());

        assert_eq!(FieldInfo {
                       datatype: "geom_msgs/Twist".into(),
                       name: "myname".into(),
                       case: FieldCase::Vector,
                   },
                   match_line("  geom_msgs/Twist [  ]   myname  # ...").unwrap().unwrap());

        assert_eq!(FieldInfo {
                       datatype: "geom_msgs/Twist".into(),
                       name: "myname".into(),
                       case: FieldCase::Array(127),
                   },
                   match_line("  geom_msgs/Twist   [   127 ]   myname# comment").unwrap().unwrap());
        assert_eq!(FieldInfo {
                       datatype: "string".into(),
                       name: "myname".into(),
                       case: FieldCase::Const("this is # data".into()),
                   },
                   match_line("  string  myname =   this is # data  ").unwrap().unwrap());
        assert_eq!(FieldInfo {
                       datatype: "geom_msgs/Twist".into(),
                       name: "myname".into(),
                       case: FieldCase::Const("-444".into()),
                   },
                   match_line("  geom_msgs/Twist  myname =   -444 # data  ").unwrap().unwrap());
    }

    #[test]
    fn match_lines_parses_real_message() {
        let data = match_lines(include_str!("TwistWithCovariance.msg")).unwrap();
        assert_eq!(vec![FieldInfo {
                            datatype: "Twist".into(),
                            name: "twist".into(),
                            case: FieldCase::Unit,
                        },
                        FieldInfo {
                            datatype: "float64".into(),
                            name: "covariance".into(),
                            case: FieldCase::Array(36),
                        }],
                   data);
    }

    #[test]
    fn parse_members_parses_real_message() {
        let data = parse_members(&match_lines(include_str!("TwistWithCovariance.msg")).unwrap())
            .unwrap();
        assert_eq!(vec![MemberInfo {
                            datatype: DataType::LocalStruct("Twist".into()),
                            name: "twist".into(),
                            case: MemberCase::Unit,
                        },
                        MemberInfo {
                            datatype: DataType::F64,
                            name: "covariance".into(),
                            case: MemberCase::Array(36),
                        }],
                   data);
    }
}
<commit_msg>Restructure message file parsing<commit_after>use regex::Regex;

static IGNORE_WHITESPACE: &'static str = r"\s*";
static ANY_WHITESPACE: &'static str = r"\s+";
static FIELD_TYPE: &'static str = r"([a-zA-Z0-9_/]+)";
static FIELD_NAME: &'static str = r"([a-zA-Z][a-zA-Z0-9_]*)";
static EMPTY_BRACKETS: &'static str = r"\[\s*\]";
static NUMBER_BRACKETS: &'static str = r"\[\s*([0-9]+)\s*\]";

fn match_field(data: &str) -> Option<FieldLine> {
    lazy_static! {
        static ref MATCHER: String = format!("^{}{}{}$", FIELD_TYPE, ANY_WHITESPACE, FIELD_NAME);
        static ref RE: Regex = Regex::new(&MATCHER).unwrap();
    }
    let captures = match RE.captures(data) {
        Some(v) => v,
        None => return None,
    };
    Some(FieldLine {
        field_type: captures.get(1).unwrap().as_str().into(),
        field_name: captures.get(2).unwrap().as_str().into(),
    })
}

fn match_vector_field(data: &str) -> Option<FieldLine> {
    lazy_static! {
        static ref MATCHER: String = format!(
            "^{}{}{}{}{}$", FIELD_TYPE, IGNORE_WHITESPACE, EMPTY_BRACKETS, ANY_WHITESPACE,
            FIELD_NAME);
        static ref RE: Regex = Regex::new(&MATCHER).unwrap();
    }
    let captures = match RE.captures(data) {
        Some(v) => v,
        None => return None,
    };
    Some(FieldLine {
        field_type: captures.get(1).unwrap().as_str().into(),
        field_name: captures.get(2).unwrap().as_str().into(),
    })
}

fn match_array_field(data: &str) -> Option<(FieldLine, usize)> {
    lazy_static! {
        static ref MATCHER: String = format!(
            "^{}{}{}{}{}$", FIELD_TYPE, IGNORE_WHITESPACE, NUMBER_BRACKETS, ANY_WHITESPACE,
            FIELD_NAME);
        static ref RE: Regex = Regex::new(&MATCHER).unwrap();
    }
    let captures = match RE.captures(data) {
        Some(v) => v,
        None => return None,
    };
    Some((FieldLine {
              field_type: captures.get(1).unwrap().as_str().into(),
              field_name: captures.get(3).unwrap().as_str().into(),
          },
          captures.get(2).unwrap().as_str().parse().unwrap()))
}

fn match_const_string(data: &str) -> Option<(FieldLine, String)> {
    lazy_static! {
        static ref MATCHER: String = format!(
            r"^(string){}{}{}={}(.*)$", ANY_WHITESPACE, FIELD_NAME, IGNORE_WHITESPACE,
            IGNORE_WHITESPACE);
        static ref RE: Regex = Regex::new(&MATCHER).unwrap();
    }
    let captures = match RE.captures(data) {
        Some(v) => v,
        None => return None,
    };
    Some((FieldLine {
              field_type: captures.get(1).unwrap().as_str().into(),
              field_name: captures.get(2).unwrap().as_str().into(),
          },
          captures.get(3).unwrap().as_str().into()))
}

fn match_const_numeric(data: &str) -> Option<(FieldLine, String)> {
    lazy_static! {
        static ref MATCHER: String = format!(
            r"^{}{}{}{}={}(-?[0-9]+)$", FIELD_TYPE, ANY_WHITESPACE, FIELD_NAME,
            IGNORE_WHITESPACE, IGNORE_WHITESPACE);
        static ref RE: Regex = Regex::new(&MATCHER).unwrap();
    }
    let captures = match RE.captures(data) {
        Some(v) => v,
        None => return None,
    };
    Some((FieldLine {
              field_type: captures.get(1).unwrap().as_str().into(),
              field_name: captures.get(2).unwrap().as_str().into(),
          },
          captures.get(3).unwrap().as_str().into()))
}

fn match_line(data: &str) -> Option<Result<FieldInfo, ()>> {
    if let Some((info, data)) = match_const_string(data.trim()) {
        return Some(FieldInfo::new(&info.field_type, &info.field_name, FieldCase::Const(data)));
    }
    let data = data.splitn(2, '#').next().unwrap().trim();
    if data == "" {
        return None;
    }
    if let Some(info) = match_field(data) {
        return Some(FieldInfo::new(&info.field_type, &info.field_name, FieldCase::Unit));
    }
    if let Some(info) = match_vector_field(data) {
        return Some(FieldInfo::new(&info.field_type, &info.field_name, FieldCase::Vector));
    }
    if let Some((info, count)) = match_array_field(data) {
        return Some(FieldInfo::new(&info.field_type, &info.field_name, FieldCase::Array(count)));
    }
    if let Some((info, data)) = match_const_numeric(data) {
        return Some(FieldInfo::new(&info.field_type, &info.field_name, FieldCase::Const(data)));
    }
    Some(Err(()))
}

#[inline]
fn match_lines(data: &str) -> Result<Vec<FieldInfo>, ()> {
    data.split('\n').filter_map(match_line).collect()
}

#[derive(Debug,PartialEq)]
struct FieldLine {
    field_type: String,
    field_name: String,
}

#[derive(Debug,PartialEq)]
enum FieldCase {
    Unit,
    Vector,
    Array(usize),
    Const(String),
}

#[derive(Debug,PartialEq)]
struct FieldInfo {
    datatype: DataType,
    name: String,
    case: FieldCase,
}

impl FieldInfo {
    fn new(datatype: &str, name: &str, case: FieldCase) -> Result<FieldInfo, ()> {
        Ok(FieldInfo {
            datatype: parse_datatype(&datatype).ok_or(())?,
            name: name.to_owned(),
            case: case,
        })
    }
}

#[derive(Debug,PartialEq)]
enum DataType {
    Bool,
    I8,
    I16,
    I32,
    I64,
    U8,
    U16,
    U32,
    U64,
    F32,
    F64,
    String,
    Time,
    Duration,
    LocalStruct(String),
    RemoteStruct(String, String),
}

fn parse_datatype(datatype: &str) -> Option<DataType> {
    match datatype {
        "bool" => Some(DataType::Bool),
        "int8" | "byte" => Some(DataType::I8),
        "int16" => Some(DataType::I16),
        "int32" => Some(DataType::I32),
        "int64" => Some(DataType::I64),
        "uint8" | "char" => Some(DataType::U8),
        "uint16" => Some(DataType::U16),
        "uint32" => Some(DataType::U32),
        "uint64" => Some(DataType::U64),
        "float32" => Some(DataType::F32),
        "float64" => Some(DataType::F64),
        "string" => Some(DataType::String),
        "time" => Some(DataType::Time),
        "duration" => Some(DataType::Duration),
        _ => {
            let parts = datatype.split('/').collect::<Vec<_>>();
            if parts.iter().any(|v| v.len() == 0) {
                return None;
            }
            match parts.len() {
                2 => Some(DataType::RemoteStruct(parts[0].to_owned(), parts[1].to_owned())),
                1 => Some(DataType::LocalStruct(parts[0].to_owned())),
                _ => None,
            }
        }
    }
}

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

    #[test]
    fn match_field_matches_legal_field() {
        assert_eq!(FieldLine {
                       field_type: "geom_msgs/Twist".into(),
                       field_name: "myname".into(),
                   },
                   match_field("geom_msgs/Twist   myname").unwrap());
    }

    #[test]
    fn match_vector_field_matches_legal_field() {
        assert_eq!(FieldLine {
                       field_type: "geom_msgs/Twist".into(),
                       field_name: "myname".into(),
                   },
                   match_vector_field("geom_msgs/Twist [  ]   myname").unwrap());
    }

    #[test]
    fn match_array_field_matches_legal_field() {
        assert_eq!((FieldLine {
                        field_type: "geom_msgs/Twist".into(),
                        field_name: "myname".into(),
                    },
                    127),
                   match_array_field("geom_msgs/Twist   [   127 ]   myname").unwrap());
    }

    #[test]
    fn match_const_string_matches_legal_field() {
        assert_eq!((FieldLine {
                        field_type: "string".into(),
                        field_name: "myname".into(),
                    },
                    "this is # data".into()),
                   match_const_string("string   myname  =  this is # data").unwrap());
    }

    #[test]
    fn match_const_numeric_matches_legal_field() {
        assert_eq!((FieldLine {
                        field_type: "mytype".into(),
                        field_name: "myname".into(),
                    },
                    "-444".into()),
                   match_const_numeric("mytype   myname  =  -444").unwrap());
    }

    #[test]
    fn match_line_works_on_legal_data() {
        assert!(match_line("#just a comment").is_none());
        assert!(match_line("#  YOLO !   ").is_none());
        assert!(match_line("      ").is_none());

        assert_eq!(FieldInfo {
                       datatype: DataType::RemoteStruct("geom_msgs".into(), "Twist".into()),
                       name: "myname".into(),
                       case: FieldCase::Unit,
                   },
                   match_line("  geom_msgs/Twist   myname    # this clearly should succeed")
                       .unwrap()
                       .unwrap());

        assert_eq!(FieldInfo {
                       datatype: DataType::RemoteStruct("geom_msgs".into(), "Twist".into()),
                       name: "myname".into(),
                       case: FieldCase::Vector,
                   },
                   match_line("  geom_msgs/Twist [  ]   myname  # ...").unwrap().unwrap());

        assert_eq!(FieldInfo {
                       datatype: DataType::U8,
                       name: "myname".into(),
                       case: FieldCase::Array(127),
                   },
                   match_line("  char   [   127 ]   myname# comment").unwrap().unwrap());
        assert_eq!(FieldInfo {
                       datatype: DataType::String,
                       name: "myname".into(),
                       case: FieldCase::Const("this is # data".into()),
                   },
                   match_line("  string  myname =   this is # data  ").unwrap().unwrap());
        assert_eq!(FieldInfo {
                       datatype: DataType::RemoteStruct("geom_msgs".into(), "Twist".into()),
                       name: "myname".into(),
                       case: FieldCase::Const("-444".into()),
                   },
                   match_line("  geom_msgs/Twist  myname =   -444 # data  ").unwrap().unwrap());
    }

    #[test]
    fn match_lines_parses_real_message() {
        let data = match_lines(include_str!("TwistWithCovariance.msg")).unwrap();
        assert_eq!(vec![FieldInfo {
                            datatype: DataType::LocalStruct("Twist".into()),
                            name: "twist".into(),
                            case: FieldCase::Unit,
                        },
                        FieldInfo {
                            datatype: DataType::F64,
                            name: "covariance".into(),
                            case: FieldCase::Array(36),
                        }],
                   data);
    }
}
<|endoftext|>


<commit_before>//! Detecting language items.
//!
//! Language items are items that represent concepts intrinsic to the language
//! itself. Examples are:
//!
//! * Traits that specify "kinds"; e.g., `Sync`, `Send`.
//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`.
//! * Functions called by the compiler itself.

pub use self::LangItem::*;

use crate::Target;

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable_Generic;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use syntax::ast;

use lazy_static::lazy_static;

// The actual lang items defined come at the end of this file in one handy table.
// So you probably just want to nip down to the end.
macro_rules! language_item_table {
    (
        $( $variant:ident, $name:expr, $method:ident, $target:path; )*
    ) => {

enum_from_u32! {
    /// A representation of all the valid language items in Rust.
    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
    pub enum LangItem {
        $($variant,)*
    }
}

impl LangItem {
    /// Returns the `name` in `#[lang = "$name"]`.
    /// For example, `LangItem::EqTraitLangItem`,
    /// that is `#[lang = "eq"]` would result in `"eq"`.
    pub fn name(self) -> &'static str {
        match self {
            $( $variant => $name, )*
        }
    }
}

#[derive(HashStable_Generic)]
pub struct LanguageItems {
    /// Mappings from lang items to their possibly found `DefId`s.
    /// The index corresponds to the order in `LangItem`.
    pub items: Vec<Option<DefId>>,
    /// Lang items that were not found during collection.
    pub missing: Vec<LangItem>,
}

impl LanguageItems {
    /// Construct an empty collection of lang items and no missing ones.
    pub fn new() -> Self {
        fn init_none(_: LangItem) -> Option<DefId> { None }

        Self {
            items: vec![$(init_none($variant)),*],
            missing: Vec::new(),
        }
    }

    /// Returns the mappings to the possibly found `DefId`s for each lang item.
    pub fn items(&self) -> &[Option<DefId>] {
        &*self.items
    }

    /// Requires that a given `LangItem` was bound and returns the corresponding `DefId`.
    /// If it wasn't bound, e.g. due to a missing `#[lang = "<it.name()>"]`,
    /// returns an error message as a string.
    pub fn require(&self, it: LangItem) -> Result<DefId, String> {
        self.items[it as usize].ok_or_else(|| format!("requires `{}` lang_item", it.name()))
    }

    $(
        /// Returns the corresponding `DefId` for the lang item
        #[doc = $name]
        /// if it exists.
        #[allow(dead_code)]
        pub fn $method(&self) -> Option<DefId> {
            self.items[$variant as usize]
        }
    )*
}

lazy_static! {
    /// A mapping from the name of the lang item to its order and the form it must be of.
    pub static ref ITEM_REFS: FxHashMap<&'static str, (usize, Target)> = {
        let mut item_refs = FxHashMap::default();
        $( item_refs.insert($name, ($variant as usize, $target)); )*
        item_refs
    };
}

// End of the macro
    }
}

impl<CTX> HashStable<CTX> for LangItem {
    fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) {
        ::std::hash::Hash::hash(self, hasher);
    }
}

/// Extracts the first `lang = "$name"` out of a list of attributes.
/// The attributes `#[panic_handler]` and `#[alloc_error_handler]`
/// are also extracted out when found.
pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
    attrs.iter().find_map(|attr| {
        Some(match attr {
            _ if attr.check_name(sym::lang) => (attr.value_str()?, attr.span),
            _ if attr.check_name(sym::panic_handler) => (sym::panic_impl, attr.span),
            _ if attr.check_name(sym::alloc_error_handler) => (sym::oom, attr.span),
            _ => return None,
        })
    })
}

language_item_table! {
//  Variant name,                Name,                 Method name,             Target;
    BoolImplItem,                "bool",               bool_impl,               Target::Impl;
    CharImplItem,                "char",               char_impl,               Target::Impl;
    StrImplItem,                 "str",                str_impl,                Target::Impl;
    SliceImplItem,               "slice",              slice_impl,              Target::Impl;
    SliceU8ImplItem,             "slice_u8",           slice_u8_impl,           Target::Impl;
    StrAllocImplItem,            "str_alloc",          str_alloc_impl,          Target::Impl;
    SliceAllocImplItem,          "slice_alloc",        slice_alloc_impl,        Target::Impl;
    SliceU8AllocImplItem,        "slice_u8_alloc",     slice_u8_alloc_impl,     Target::Impl;
    ConstPtrImplItem,            "const_ptr",          const_ptr_impl,          Target::Impl;
    MutPtrImplItem,              "mut_ptr",            mut_ptr_impl,            Target::Impl;
    I8ImplItem,                  "i8",                 i8_impl,                 Target::Impl;
    I16ImplItem,                 "i16",                i16_impl,                Target::Impl;
    I32ImplItem,                 "i32",                i32_impl,                Target::Impl;
    I64ImplItem,                 "i64",                i64_impl,                Target::Impl;
    I128ImplItem,                "i128",               i128_impl,               Target::Impl;
    IsizeImplItem,               "isize",              isize_impl,              Target::Impl;
    U8ImplItem,                  "u8",                 u8_impl,                 Target::Impl;
    U16ImplItem,                 "u16",                u16_impl,                Target::Impl;
    U32ImplItem,                 "u32",                u32_impl,                Target::Impl;
    U64ImplItem,                 "u64",                u64_impl,                Target::Impl;
    U128ImplItem,                "u128",               u128_impl,               Target::Impl;
    UsizeImplItem,               "usize",              usize_impl,              Target::Impl;
    F32ImplItem,                 "f32",                f32_impl,                Target::Impl;
    F64ImplItem,                 "f64",                f64_impl,                Target::Impl;
    F32RuntimeImplItem,          "f32_runtime",        f32_runtime_impl,        Target::Impl;
    F64RuntimeImplItem,          "f64_runtime",        f64_runtime_impl,        Target::Impl;

    SizedTraitLangItem,          "sized",              sized_trait,             Target::Trait;
    UnsizeTraitLangItem,         "unsize",             unsize_trait,            Target::Trait;
    // trait injected by #[derive(PartialEq)], (i.e. "Partial EQ").
    StructuralPeqTraitLangItem,  "structural_peq",     structural_peq_trait,    Target::Trait;
    // trait injected by #[derive(Eq)], (i.e. "Total EQ"; no, I will not apologize).
    StructuralTeqTraitLangItem,  "structural_teq",     structural_teq_trait,    Target::Trait;
    CopyTraitLangItem,           "copy",               copy_trait,              Target::Trait;
    CloneTraitLangItem,          "clone",              clone_trait,             Target::Trait;
    SyncTraitLangItem,           "sync",               sync_trait,              Target::Trait;
    FreezeTraitLangItem,         "freeze",             freeze_trait,            Target::Trait;

    DropTraitLangItem,           "drop",               drop_trait,              Target::Trait;

    CoerceUnsizedTraitLangItem,  "coerce_unsized",     coerce_unsized_trait,    Target::Trait;
    DispatchFromDynTraitLangItem,"dispatch_from_dyn",  dispatch_from_dyn_trait, Target::Trait;

    AddTraitLangItem,            "add",                add_trait,               Target::Trait;
    SubTraitLangItem,            "sub",                sub_trait,               Target::Trait;
    MulTraitLangItem,            "mul",                mul_trait,               Target::Trait;
    DivTraitLangItem,            "div",                div_trait,               Target::Trait;
    RemTraitLangItem,            "rem",                rem_trait,               Target::Trait;
    NegTraitLangItem,            "neg",                neg_trait,               Target::Trait;
    NotTraitLangItem,            "not",                not_trait,               Target::Trait;
    BitXorTraitLangItem,         "bitxor",             bitxor_trait,            Target::Trait;
    BitAndTraitLangItem,         "bitand",             bitand_trait,            Target::Trait;
    BitOrTraitLangItem,          "bitor",              bitor_trait,             Target::Trait;
    ShlTraitLangItem,            "shl",                shl_trait,               Target::Trait;
    ShrTraitLangItem,            "shr",                shr_trait,               Target::Trait;
    AddAssignTraitLangItem,      "add_assign",         add_assign_trait,        Target::Trait;
    SubAssignTraitLangItem,      "sub_assign",         sub_assign_trait,        Target::Trait;
    MulAssignTraitLangItem,      "mul_assign",         mul_assign_trait,        Target::Trait;
    DivAssignTraitLangItem,      "div_assign",         div_assign_trait,        Target::Trait;
    RemAssignTraitLangItem,      "rem_assign",         rem_assign_trait,        Target::Trait;
    BitXorAssignTraitLangItem,   "bitxor_assign",      bitxor_assign_trait,     Target::Trait;
    BitAndAssignTraitLangItem,   "bitand_assign",      bitand_assign_trait,     Target::Trait;
    BitOrAssignTraitLangItem,    "bitor_assign",       bitor_assign_trait,      Target::Trait;
    ShlAssignTraitLangItem,      "shl_assign",         shl_assign_trait,        Target::Trait;
    ShrAssignTraitLangItem,      "shr_assign",         shr_assign_trait,        Target::Trait;
    IndexTraitLangItem,          "index",              index_trait,             Target::Trait;
    IndexMutTraitLangItem,       "index_mut",          index_mut_trait,         Target::Trait;

    UnsafeCellTypeLangItem,      "unsafe_cell",        unsafe_cell_type,        Target::Struct;
    VaListTypeLangItem,          "va_list",            va_list,                 Target::Struct;

    DerefTraitLangItem,          "deref",              deref_trait,             Target::Trait;
    DerefMutTraitLangItem,       "deref_mut",          deref_mut_trait,         Target::Trait;
    ReceiverTraitLangItem,       "receiver",           receiver_trait,          Target::Trait;

    FnTraitLangItem,             "fn",                 fn_trait,                Target::Trait;
    FnMutTraitLangItem,          "fn_mut",             fn_mut_trait,            Target::Trait;
    FnOnceTraitLangItem,         "fn_once",            fn_once_trait,           Target::Trait;

    FutureTraitLangItem,         "future_trait",       future_trait,            Target::Trait;
    GeneratorStateLangItem,      "generator_state",    gen_state,               Target::Enum;
    GeneratorTraitLangItem,      "generator",          gen_trait,               Target::Trait;
    UnpinTraitLangItem,          "unpin",              unpin_trait,             Target::Trait;
    PinTypeLangItem,             "pin",                pin_type,                Target::Struct;

    // Don't be fooled by the naming here: this lang item denotes `PartialEq`, not `Eq`.
    EqTraitLangItem,             "eq",                 eq_trait,                Target::Trait;
    PartialOrdTraitLangItem,     "partial_ord",        partial_ord_trait,       Target::Trait;

    // A number of panic-related lang items. The `panic` item corresponds to
    // divide-by-zero and various panic cases with `match`. The
    // `panic_bounds_check` item is for indexing arrays.
    //
    // The `begin_unwind` lang item has a predefined symbol name and is sort of
    // a "weak lang item" in the sense that a crate is not required to have it
    // defined to use it, but a final product is required to define it
    // somewhere. Additionally, there are restrictions on crates that use a weak
    // lang item, but do not have it defined.
    PanicFnLangItem,             "panic",              panic_fn,                Target::Fn;
    PanicBoundsCheckFnLangItem,  "panic_bounds_check", panic_bounds_check_fn,   Target::Fn;
    PanicInfoLangItem,           "panic_info",         panic_info,              Target::Struct;
    PanicLocationLangItem,       "panic_location",     panic_location,          Target::Struct;
    PanicImplLangItem,           "panic_impl",         panic_impl,              Target::Fn;
    // Libstd panic entry point. Necessary for const eval to be able to catch it
    BeginPanicFnLangItem,        "begin_panic",        begin_panic_fn,          Target::Fn;

    ExchangeMallocFnLangItem,    "exchange_malloc",    exchange_malloc_fn,      Target::Fn;
    BoxFreeFnLangItem,           "box_free",           box_free_fn,             Target::Fn;
    DropInPlaceFnLangItem,       "drop_in_place",      drop_in_place_fn,        Target::Fn;
    OomLangItem,                 "oom",                oom,                     Target::Fn;
    AllocLayoutLangItem,         "alloc_layout",       alloc_layout,            Target::Struct;

    StartFnLangItem,             "start",              start_fn,                Target::Fn;

    EhPersonalityLangItem,       "eh_personality",     eh_personality,          Target::Fn;
    EhUnwindResumeLangItem,      "eh_unwind_resume",   eh_unwind_resume,        Target::Fn;
    EhCatchTypeinfoLangItem,     "eh_catch_typeinfo",  eh_catch_typeinfo,       Target::Static;

    OwnedBoxLangItem,            "owned_box",          owned_box,               Target::Struct;

    PhantomDataItem,             "phantom_data",       phantom_data,            Target::Struct;

    ManuallyDropItem,            "manually_drop",      manually_drop,           Target::Struct;

    MaybeUninitLangItem,         "maybe_uninit",       maybe_uninit,            Target::Union;

    // Align offset for stride != 1; must not panic.
    AlignOffsetLangItem,         "align_offset",       align_offset_fn,         Target::Fn;

    TerminationTraitLangItem,    "termination",        termination,             Target::Trait;

    Arc,                         "arc",                arc,                     Target::Struct;
    Rc,                          "rc",                 rc,                      Target::Struct;
}
<commit_msg>Nits.<commit_after>//! Defines language items.
//!
//! Language items are items that represent concepts intrinsic to the language
//! itself. Examples are:
//!
//! * Traits that specify "kinds"; e.g., `Sync`, `Send`.
//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`.
//! * Functions called by the compiler itself.

pub use self::LangItem::*;

use crate::Target;

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable_Generic;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use syntax::ast;

use lazy_static::lazy_static;

// The actual lang items defined come at the end of this file in one handy table.
// So you probably just want to nip down to the end.
macro_rules! language_item_table {
    (
        $( $variant:ident, $name:expr, $method:ident, $target:path; )*
    ) => {

enum_from_u32! {
    /// A representation of all the valid language items in Rust.
    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
    pub enum LangItem {
        $($variant,)*
    }
}

impl LangItem {
    /// Returns the `name` in `#[lang = "$name"]`.
    /// For example, `LangItem::EqTraitLangItem`,
    /// that is `#[lang = "eq"]` would result in `"eq"`.
    pub fn name(self) -> &'static str {
        match self {
            $( $variant => $name, )*
        }
    }
}

#[derive(HashStable_Generic)]
pub struct LanguageItems {
    /// Mappings from lang items to their possibly found `DefId`s.
    /// The index corresponds to the order in `LangItem`.
    pub items: Vec<Option<DefId>>,
    /// Lang items that were not found during collection.
    pub missing: Vec<LangItem>,
}

impl LanguageItems {
    /// Construct an empty collection of lang items and no missing ones.
    pub fn new() -> Self {
        fn init_none(_: LangItem) -> Option<DefId> { None }

        Self {
            items: vec![$(init_none($variant)),*],
            missing: Vec::new(),
        }
    }

    /// Returns the mappings to the possibly found `DefId`s for each lang item.
    pub fn items(&self) -> &[Option<DefId>] {
        &*self.items
    }

    /// Requires that a given `LangItem` was bound and returns the corresponding `DefId`.
    /// If it wasn't bound, e.g. due to a missing `#[lang = "<it.name()>"]`,
    /// returns an error message as a string.
    pub fn require(&self, it: LangItem) -> Result<DefId, String> {
        self.items[it as usize].ok_or_else(|| format!("requires `{}` lang_item", it.name()))
    }

    $(
        /// Returns the corresponding `DefId` for the lang item
        #[doc = $name]
        /// if it exists.
        #[allow(dead_code)]
        pub fn $method(&self) -> Option<DefId> {
            self.items[$variant as usize]
        }
    )*
}

lazy_static! {
    /// A mapping from the name of the lang item to its order and the form it must be of.
    pub static ref ITEM_REFS: FxHashMap<&'static str, (usize, Target)> = {
        let mut item_refs = FxHashMap::default();
        $( item_refs.insert($name, ($variant as usize, $target)); )*
        item_refs
    };
}

// End of the macro
    }
}

impl<CTX> HashStable<CTX> for LangItem {
    fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) {
        ::std::hash::Hash::hash(self, hasher);
    }
}

/// Extracts the first `lang = "$name"` out of a list of attributes.
/// The attributes `#[panic_handler]` and `#[alloc_error_handler]`
/// are also extracted out when found.
pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
    attrs.iter().find_map(|attr| {
        Some(match attr {
            _ if attr.check_name(sym::lang) => (attr.value_str()?, attr.span),
            _ if attr.check_name(sym::panic_handler) => (sym::panic_impl, attr.span),
            _ if attr.check_name(sym::alloc_error_handler) => (sym::oom, attr.span),
            _ => return None,
        })
    })
}

language_item_table! {
//  Variant name,                Name,                 Method name,             Target;
    BoolImplItem,                "bool",               bool_impl,               Target::Impl;
    CharImplItem,                "char",               char_impl,               Target::Impl;
    StrImplItem,                 "str",                str_impl,                Target::Impl;
    SliceImplItem,               "slice",              slice_impl,              Target::Impl;
    SliceU8ImplItem,             "slice_u8",           slice_u8_impl,           Target::Impl;
    StrAllocImplItem,            "str_alloc",          str_alloc_impl,          Target::Impl;
    SliceAllocImplItem,          "slice_alloc",        slice_alloc_impl,        Target::Impl;
    SliceU8AllocImplItem,        "slice_u8_alloc",     slice_u8_alloc_impl,     Target::Impl;
    ConstPtrImplItem,            "const_ptr",          const_ptr_impl,          Target::Impl;
    MutPtrImplItem,              "mut_ptr",            mut_ptr_impl,            Target::Impl;
    I8ImplItem,                  "i8",                 i8_impl,                 Target::Impl;
    I16ImplItem,                 "i16",                i16_impl,                Target::Impl;
    I32ImplItem,                 "i32",                i32_impl,                Target::Impl;
    I64ImplItem,                 "i64",                i64_impl,                Target::Impl;
    I128ImplItem,                "i128",               i128_impl,               Target::Impl;
    IsizeImplItem,               "isize",              isize_impl,              Target::Impl;
    U8ImplItem,                  "u8",                 u8_impl,                 Target::Impl;
    U16ImplItem,                 "u16",                u16_impl,                Target::Impl;
    U32ImplItem,                 "u32",                u32_impl,                Target::Impl;
    U64ImplItem,                 "u64",                u64_impl,                Target::Impl;
    U128ImplItem,                "u128",               u128_impl,               Target::Impl;
    UsizeImplItem,               "usize",              usize_impl,              Target::Impl;
    F32ImplItem,                 "f32",                f32_impl,                Target::Impl;
    F64ImplItem,                 "f64",                f64_impl,                Target::Impl;
    F32RuntimeImplItem,          "f32_runtime",        f32_runtime_impl,        Target::Impl;
    F64RuntimeImplItem,          "f64_runtime",        f64_runtime_impl,        Target::Impl;

    SizedTraitLangItem,          "sized",              sized_trait,             Target::Trait;
    UnsizeTraitLangItem,         "unsize",             unsize_trait,            Target::Trait;
    // trait injected by #[derive(PartialEq)], (i.e. "Partial EQ").
    StructuralPeqTraitLangItem,  "structural_peq",     structural_peq_trait,    Target::Trait;
    // trait injected by #[derive(Eq)], (i.e. "Total EQ"; no, I will not apologize).
    StructuralTeqTraitLangItem,  "structural_teq",     structural_teq_trait,    Target::Trait;
    CopyTraitLangItem,           "copy",               copy_trait,              Target::Trait;
    CloneTraitLangItem,          "clone",              clone_trait,             Target::Trait;
    SyncTraitLangItem,           "sync",               sync_trait,              Target::Trait;
    FreezeTraitLangItem,         "freeze",             freeze_trait,            Target::Trait;

    DropTraitLangItem,           "drop",               drop_trait,              Target::Trait;

    CoerceUnsizedTraitLangItem,  "coerce_unsized",     coerce_unsized_trait,    Target::Trait;
    DispatchFromDynTraitLangItem,"dispatch_from_dyn",  dispatch_from_dyn_trait, Target::Trait;

    AddTraitLangItem,            "add",                add_trait,               Target::Trait;
    SubTraitLangItem,            "sub",                sub_trait,               Target::Trait;
    MulTraitLangItem,            "mul",                mul_trait,               Target::Trait;
    DivTraitLangItem,            "div",                div_trait,               Target::Trait;
    RemTraitLangItem,            "rem",                rem_trait,               Target::Trait;
    NegTraitLangItem,            "neg",                neg_trait,               Target::Trait;
    NotTraitLangItem,            "not",                not_trait,               Target::Trait;
    BitXorTraitLangItem,         "bitxor",             bitxor_trait,            Target::Trait;
    BitAndTraitLangItem,         "bitand",             bitand_trait,            Target::Trait;
    BitOrTraitLangItem,          "bitor",              bitor_trait,             Target::Trait;
    ShlTraitLangItem,            "shl",                shl_trait,               Target::Trait;
    ShrTraitLangItem,            "shr",                shr_trait,               Target::Trait;
    AddAssignTraitLangItem,      "add_assign",         add_assign_trait,        Target::Trait;
    SubAssignTraitLangItem,      "sub_assign",         sub_assign_trait,        Target::Trait;
    MulAssignTraitLangItem,      "mul_assign",         mul_assign_trait,        Target::Trait;
    DivAssignTraitLangItem,      "div_assign",         div_assign_trait,        Target::Trait;
    RemAssignTraitLangItem,      "rem_assign",         rem_assign_trait,        Target::Trait;
    BitXorAssignTraitLangItem,   "bitxor_assign",      bitxor_assign_trait,     Target::Trait;
    BitAndAssignTraitLangItem,   "bitand_assign",      bitand_assign_trait,     Target::Trait;
    BitOrAssignTraitLangItem,    "bitor_assign",       bitor_assign_trait,      Target::Trait;
    ShlAssignTraitLangItem,      "shl_assign",         shl_assign_trait,        Target::Trait;
    ShrAssignTraitLangItem,      "shr_assign",         shr_assign_trait,        Target::Trait;
    IndexTraitLangItem,          "index",              index_trait,             Target::Trait;
    IndexMutTraitLangItem,       "index_mut",          index_mut_trait,         Target::Trait;

    UnsafeCellTypeLangItem,      "unsafe_cell",        unsafe_cell_type,        Target::Struct;
    VaListTypeLangItem,          "va_list",            va_list,                 Target::Struct;

    DerefTraitLangItem,          "deref",              deref_trait,             Target::Trait;
    DerefMutTraitLangItem,       "deref_mut",          deref_mut_trait,         Target::Trait;
    ReceiverTraitLangItem,       "receiver",           receiver_trait,          Target::Trait;

    FnTraitLangItem,             "fn",                 fn_trait,                Target::Trait;
    FnMutTraitLangItem,          "fn_mut",             fn_mut_trait,            Target::Trait;
    FnOnceTraitLangItem,         "fn_once",            fn_once_trait,           Target::Trait;

    FutureTraitLangItem,         "future_trait",       future_trait,            Target::Trait;
    GeneratorStateLangItem,      "generator_state",    gen_state,               Target::Enum;
    GeneratorTraitLangItem,      "generator",          gen_trait,               Target::Trait;
    UnpinTraitLangItem,          "unpin",              unpin_trait,             Target::Trait;
    PinTypeLangItem,             "pin",                pin_type,                Target::Struct;

    // Don't be fooled by the naming here: this lang item denotes `PartialEq`, not `Eq`.
    EqTraitLangItem,             "eq",                 eq_trait,                Target::Trait;
    PartialOrdTraitLangItem,     "partial_ord",        partial_ord_trait,       Target::Trait;

    // A number of panic-related lang items. The `panic` item corresponds to
    // divide-by-zero and various panic cases with `match`. The
    // `panic_bounds_check` item is for indexing arrays.
    //
    // The `begin_unwind` lang item has a predefined symbol name and is sort of
    // a "weak lang item" in the sense that a crate is not required to have it
    // defined to use it, but a final product is required to define it
    // somewhere. Additionally, there are restrictions on crates that use a weak
    // lang item, but do not have it defined.
    PanicFnLangItem,             "panic",              panic_fn,                Target::Fn;
    PanicBoundsCheckFnLangItem,  "panic_bounds_check", panic_bounds_check_fn,   Target::Fn;
    PanicInfoLangItem,           "panic_info",         panic_info,              Target::Struct;
    PanicLocationLangItem,       "panic_location",     panic_location,          Target::Struct;
    PanicImplLangItem,           "panic_impl",         panic_impl,              Target::Fn;
    // Libstd panic entry point. Necessary for const eval to be able to catch it
    BeginPanicFnLangItem,        "begin_panic",        begin_panic_fn,          Target::Fn;

    ExchangeMallocFnLangItem,    "exchange_malloc",    exchange_malloc_fn,      Target::Fn;
    BoxFreeFnLangItem,           "box_free",           box_free_fn,             Target::Fn;
    DropInPlaceFnLangItem,       "drop_in_place",      drop_in_place_fn,        Target::Fn;
    OomLangItem,                 "oom",                oom,                     Target::Fn;
    AllocLayoutLangItem,         "alloc_layout",       alloc_layout,            Target::Struct;

    StartFnLangItem,             "start",              start_fn,                Target::Fn;

    EhPersonalityLangItem,       "eh_personality",     eh_personality,          Target::Fn;
    EhUnwindResumeLangItem,      "eh_unwind_resume",   eh_unwind_resume,        Target::Fn;
    EhCatchTypeinfoLangItem,     "eh_catch_typeinfo",  eh_catch_typeinfo,       Target::Static;

    OwnedBoxLangItem,            "owned_box",          owned_box,               Target::Struct;

    PhantomDataItem,             "phantom_data",       phantom_data,            Target::Struct;

    ManuallyDropItem,            "manually_drop",      manually_drop,           Target::Struct;

    MaybeUninitLangItem,         "maybe_uninit",       maybe_uninit,            Target::Union;

    // Align offset for stride != 1; must not panic.
    AlignOffsetLangItem,         "align_offset",       align_offset_fn,         Target::Fn;

    TerminationTraitLangItem,    "termination",        termination,             Target::Trait;

    Arc,                         "arc",                arc,                     Target::Struct;
    Rc,                          "rc",                 rc,                      Target::Struct;
}
<|endoftext|>


<commit_before>//! Hook into libgraphviz for rendering dataflow graphs for MIR.

use rustc_hir::def_id::DefId;
use rustc_middle::mir::{BasicBlock, Body};

use std::fs;
use std::io;
use std::marker::PhantomData;
use std::path::Path;

use crate::util::graphviz_safe_def_name;

use super::DataflowBuilder;
use super::DebugFormatted;
use super::{BitDenotation, DataflowState};

pub trait MirWithFlowState<'tcx> {
    type BD: BitDenotation<'tcx>;
    fn def_id(&self) -> DefId;
    fn body(&self) -> &Body<'tcx>;
    fn flow_state(&self) -> &DataflowState<'tcx, Self::BD>;
}

impl<'a, 'tcx, BD> MirWithFlowState<'tcx> for DataflowBuilder<'a, 'tcx, BD>
where
    BD: BitDenotation<'tcx>,
{
    type BD = BD;
    fn def_id(&self) -> DefId {
        self.def_id
    }
    fn body(&self) -> &Body<'tcx> {
        self.flow_state.body()
    }
    fn flow_state(&self) -> &DataflowState<'tcx, Self::BD> {
        &self.flow_state.flow_state
    }
}

struct Graph<'a, 'tcx, MWF, P>
where
    MWF: MirWithFlowState<'tcx>,
{
    mbcx: &'a MWF,
    phantom: PhantomData<&'tcx ()>,
    render_idx: P,
}

pub(crate) fn print_borrowck_graph_to<'a, 'tcx, BD, P>(
    mbcx: &DataflowBuilder<'a, 'tcx, BD>,
    path: &Path,
    render_idx: P,
) -> io::Result<()>
where
    BD: BitDenotation<'tcx>,
    P: Fn(&BD, BD::Idx) -> DebugFormatted,
{
    let g = Graph { mbcx, phantom: PhantomData, render_idx };
    let mut v = Vec::new();
    dot::render(&g, &mut v)?;
    debug!("print_borrowck_graph_to path: {} def_id: {:?}", path.display(), mbcx.def_id);
    fs::write(path, v)
}

pub type Node = BasicBlock;

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Edge {
    source: BasicBlock,
    index: usize,
}

fn outgoing(body: &Body<'_>, bb: BasicBlock) -> Vec<Edge> {
    (0..body[bb].terminator().successors().count())
        .map(|index| Edge { source: bb, index })
        .collect()
}

impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P>
where
    MWF: MirWithFlowState<'tcx>,
    P: Fn(&MWF::BD, <MWF::BD as BitDenotation<'tcx>>::Idx) -> DebugFormatted,
{
    type Node = Node;
    type Edge = Edge;
    fn graph_id(&self) -> dot::Id<'_> {
        let name = graphviz_safe_def_name(self.mbcx.def_id());
        dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
    }

    fn node_id(&self, n: &Node) -> dot::Id<'_> {
        dot::Id::new(format!("bb_{}", n.index())).unwrap()
    }

    fn node_label(&self, n: &Node) -> dot::LabelText<'_> {
        // Node label is something like this:
        // +---------+----------------------------------+------------------+------------------+
        // | ENTRY   | MIR                              | GEN              | KILL             |
        // +---------+----------------------------------+------------------+------------------+
        // |         |  0: StorageLive(_7)              | bb3[2]: reserved | bb2[0]: reserved |
        // |         |  1: StorageLive(_8)              | bb3[2]: active   | bb2[0]: active   |
        // |         |  2: _8 = &mut _1                 |                  | bb4[2]: reserved |
        // |         |                                  |                  | bb4[2]: active   |
        // |         |                                  |                  | bb9[0]: reserved |
        // |         |                                  |                  | bb9[0]: active   |
        // |         |                                  |                  | bb10[0]: reserved|
        // |         |                                  |                  | bb10[0]: active  |
        // |         |                                  |                  | bb11[0]: reserved|
        // |         |                                  |                  | bb11[0]: active  |
        // +---------+----------------------------------+------------------+------------------+
        // | [00-00] | _7 = const Foo::twiddle(move _8) | [0c-00]          | [f3-0f]          |
        // +---------+----------------------------------+------------------+------------------+
        let mut v = Vec::new();
        self.node_label_internal(n, &mut v, *n, self.mbcx.body()).unwrap();
        dot::LabelText::html(String::from_utf8(v).unwrap())
    }

    fn node_shape(&self, _n: &Node) -> Option<dot::LabelText<'_>> {
        Some(dot::LabelText::label("none"))
    }

    fn edge_label(&'a self, e: &Edge) -> dot::LabelText<'a> {
        let term = self.mbcx.body()[e.source].terminator();
        let label = &term.kind.fmt_successor_labels()[e.index];
        dot::LabelText::label(label.clone())
    }
}

impl<'a, 'tcx, MWF, P> Graph<'a, 'tcx, MWF, P>
where
    MWF: MirWithFlowState<'tcx>,
    P: Fn(&MWF::BD, <MWF::BD as BitDenotation<'tcx>>::Idx) -> DebugFormatted,
{
    /// Generate the node label
    fn node_label_internal<W: io::Write>(
        &self,
        n: &Node,
        w: &mut W,
        block: BasicBlock,
        body: &Body<'_>,
    ) -> io::Result<()> {
        // Header rows
        const HDRS: [&str; 4] = ["ENTRY", "MIR", "BLOCK GENS", "BLOCK KILLS"];
        const HDR_FMT: &str = "bgcolor=\"grey\"";
        write!(w, "

", HDRS.len())?;
        write!(w, "{:?}", block.index())?;
        write!(w, "

")?;
        for hdr in &HDRS {
            write!(w, "{}	", HDR_FMT, hdr)?;
        }
        write!(w, "
")?;

        // Data row
        self.node_label_verbose_row(n, w, block, body)?;
        self.node_label_final_row(n, w, block, body)?;
        write!(w, "
")?;

        Ok(())
    }

    /// Builds the verbose row: full MIR data, and detailed gen/kill/entry sets.
    fn node_label_verbose_row<W: io::Write>(
        &self,
        n: &Node,
        w: &mut W,
        block: BasicBlock,
        body: &Body<'_>,
    ) -> io::Result<()> {
        let i = n.index();

        macro_rules! dump_set_for {
            ($set:ident, $interpret:ident) => {
                write!(w, "")?;

                let flow = self.mbcx.flow_state();
                let entry_interp =
                    flow.$interpret(&flow.operator, flow.sets.$set(i), &self.render_idx);
                for e in &entry_interp {
                    write!(w, "{:?}
", e)?;
                }
                write!(w, "	")?;
            };
        }

        write!(w, "
")?;
        // Entry
        dump_set_for!(entry_set_for, interpret_set);

        // MIR statements
        write!(w, "")?;
        {
            let data = &body[block];
            for (i, statement) in data.statements.iter().enumerate() {
                write!(
                    w,
                    "{}
",
                    dot::escape_html(&format!("{:3}: {:?}", i, statement))
                )?;
            }
        }
        write!(w, "	")?;

        // Gen
        dump_set_for!(gen_set_for, interpret_hybrid_set);

        // Kill
        dump_set_for!(kill_set_for, interpret_hybrid_set);

        write!(w, "
")?;

        Ok(())
    }

    /// Builds the summary row: terminator, gen/kill/entry bit sets.
    fn node_label_final_row<W: io::Write>(
        &self,
        n: &Node,
        w: &mut W,
        block: BasicBlock,
        body: &Body<'_>,
    ) -> io::Result<()> {
        let i = n.index();

        let flow = self.mbcx.flow_state();

        write!(w, "
")?;

        // Entry
        let set = flow.sets.entry_set_for(i);
        write!(w, "{:?}	", dot::escape_html(&set.to_string()))?;

        // Terminator
        write!(w, "")?;
        {
            let data = &body[block];
            let mut terminator_head = String::new();
            data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
            write!(w, "{}", dot::escape_html(&terminator_head))?;
        }
        write!(w, "	")?;

        // Gen/Kill
        let trans = flow.sets.trans_for(i);
        write!(w, "{:?}	", dot::escape_html(&format!("{:?}", trans.gen_set)))?;
        write!(w, "{:?}	", dot::escape_html(&format!("{:?}", trans.kill_set)))?;

        write!(w, "
")?;

        Ok(())
    }
}

impl<'a, 'tcx, MWF, P> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF, P>
where
    MWF: MirWithFlowState<'tcx>,
{
    type Node = Node;
    type Edge = Edge;
    fn nodes(&self) -> dot::Nodes<'_, Node> {
        self.mbcx.body().basic_blocks().indices().collect::<Vec<_>>().into()
    }

    fn edges(&self) -> dot::Edges<'_, Edge> {
        let body = self.mbcx.body();

        body.basic_blocks().indices().flat_map(|bb| outgoing(body, bb)).collect::<Vec<_>>().into()
    }

    fn source(&self, edge: &Edge) -> Node {
        edge.source
    }

    fn target(&self, edge: &Edge) -> Node {
        let body = self.mbcx.body();
        *body[edge.source].terminator().successors().nth(edge.index).unwrap()
    }
}
<commit_msg>Rollup merge of #70707 - ecstatic-morse:dataflow-graphviz-cleanup, r=nikomatsakis<commit_after><|endoftext|>


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

use llvm::{BasicBlockRef, ValueRef};
use rustc::mir::repr as mir;
use trans::adt;
use trans::base;
use trans::build;
use trans::attributes;
use trans::common::{self, Block};
use trans::debuginfo::DebugLoc;
use trans::type_of;
use trans::type_::Type;

use super::MirContext;
use super::operand::OperandValue::{FatPtr, Immediate, Ref};

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
    pub fn trans_block(&mut self, bb: mir::BasicBlock) {
        debug!("trans_block({:?})", bb);

        let mut bcx = self.bcx(bb);
        let data = self.mir.basic_block_data(bb);

        for statement in &data.statements {
            bcx = self.trans_statement(bcx, statement);
        }

        debug!("trans_block: terminator: {:?}", data.terminator());

        match *data.terminator() {
            mir::Terminator::Goto { target } => {
                build::Br(bcx, self.llblock(target), DebugLoc::None)
            }

            mir::Terminator::If { ref cond, targets: (true_bb, false_bb) } => {
                let cond = self.trans_operand(bcx, cond);
                let lltrue = self.llblock(true_bb);
                let llfalse = self.llblock(false_bb);
                build::CondBr(bcx, cond.immediate(), lltrue, llfalse, DebugLoc::None);
            }

            mir::Terminator::Switch { ref discr, ref adt_def, ref targets } => {
                let adt_ty = bcx.tcx().lookup_item_type(adt_def.did).ty;
                let represented_ty = adt::represent_type(bcx.ccx(), adt_ty);

                let discr_lvalue = self.trans_lvalue(bcx, discr);
                let discr = adt::trans_get_discr(bcx, &represented_ty, discr_lvalue.llval, None);

                // The else branch of the Switch can't be hit, so branch to an unreachable
                // instruction so LLVM knows that
                let unreachable_blk = self.unreachable_block();

                let switch = build::Switch(bcx, discr, unreachable_blk.llbb, targets.len());
                assert_eq!(adt_def.variants.len(), targets.len());
                for (adt_variant, target) in adt_def.variants.iter().zip(targets) {
                    let llval = adt::trans_case(bcx, &*represented_ty, adt_variant.disr_val);
                    let llbb = self.llblock(*target);

                    build::AddCase(switch, llval, llbb)
                }
            }

            mir::Terminator::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
                let (otherwise, targets) = targets.split_last().unwrap();
                let discr = build::Load(bcx, self.trans_lvalue(bcx, discr).llval);
                let switch = build::Switch(bcx, discr, self.llblock(*otherwise), values.len());
                for (value, target) in values.iter().zip(targets) {
                    let llval = self.trans_constval(bcx, value, switch_ty).immediate();
                    let llbb = self.llblock(*target);
                    build::AddCase(switch, llval, llbb)
                }
            }

            mir::Terminator::Resume => {
                let ps = self.get_personality_slot(bcx);
                let lp = build::Load(bcx, ps);
                base::call_lifetime_end(bcx, ps);
                base::trans_unwind_resume(bcx, lp);
            }

            mir::Terminator::Return => {
                let return_ty = bcx.monomorphize(&self.mir.return_ty);
                base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None);
            }

            mir::Terminator::Call { ref func, ref args, ref kind } => {
                // Create the callee. This will always be a fn ptr and hence a kind of scalar.
                let callee = self.trans_operand(bcx, func);
                let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty);
                let debugloc = DebugLoc::None;
                // The arguments we'll be passing. Plus one to account for outptr, if used.
                let mut llargs = Vec::with_capacity(args.len() + 1);

                // Prepare the return value destination
                let (ret_dest_ty, must_copy_dest) = if let Some(d) = kind.destination() {
                    let dest = self.trans_lvalue(bcx, d);
                    let ret_ty = dest.ty.to_ty(bcx.tcx());
                    if type_of::return_uses_outptr(bcx.ccx(), ret_ty) {
                        llargs.push(dest.llval);
                        (Some((dest, ret_ty)), false)
                    } else {
                        (Some((dest, ret_ty)), !common::type_is_zero_size(bcx.ccx(), ret_ty))
                    }
                } else {
                    (None, false)
                };

                // Process the rest of the args.
                for arg in args {
                    match self.trans_operand(bcx, arg).val {
                        Ref(llval) | Immediate(llval) => llargs.push(llval),
                        FatPtr(b, e) => {
                            llargs.push(b);
                            llargs.push(e);
                        }
                    }
                }

                // Many different ways to call a function handled here
                match (base::avoid_invoke(bcx), kind) {
                    // The two cases below are the only ones to use LLVM’s `invoke`.
                    (false, &mir::CallKind::DivergingCleanup(cleanup)) => {
                        let cleanup = self.bcx(cleanup);
                        let landingpad = self.make_landing_pad(cleanup);
                        build::Invoke(bcx,
                                      callee.immediate(),
                                      &llargs[..],
                                      self.unreachable_block().llbb,
                                      landingpad.llbb,
                                      Some(attrs),
                                      debugloc);
                    },
                    (false, &mir::CallKind::ConvergingCleanup { ref targets, .. }) => {
                        let cleanup = self.bcx(targets.1);
                        let landingpad = self.make_landing_pad(cleanup);
                        let (target, postinvoke) = if must_copy_dest {
                            (bcx.fcx.new_block(false, "", None), Some(self.bcx(targets.0)))
                        } else {
                            (self.bcx(targets.0), None)
                        };
                        let invokeret = build::Invoke(bcx,
                                                      callee.immediate(),
                                                      &llargs[..],
                                                      target.llbb,
                                                      landingpad.llbb,
                                                      Some(attrs),
                                                      debugloc);
                        if let Some(postinvoketarget) = postinvoke {
                            // We translate the copy into a temoprary block. The temporary block is
                            // necessary because the current block has already been terminated (by
                            // `invoke`) and we cannot really translate into the target block
                            // because:
                            //  * The target block may have more than a single precedesor;
                            //  * Some LLVM insns cannot have a preceeding store insn (phi,
                            //    cleanuppad), and adding/prepending the store now may render
                            //    those other instructions invalid.
                            //
                            // NB: This approach still may break some LLVM code. For example if the
                            // target block starts with a `phi` (which may only match on immediate
                            // precedesors), it cannot know about this temporary block thus
                            // resulting in an invalid code:
                            //
                            // this:
                            //     …
                            //     %0 = …
                            //     %1 = invoke to label %temp …
                            // temp:
                            //     store ty %1, ty* %dest
                            //     br label %actualtargetblock
                            // actualtargetblock:            ; preds: %temp, …
                            //     phi … [%this, …], [%0, …] ; ERROR: phi requires to match only on
                            //                               ; immediate precedesors
                            let (ret_dest, ret_ty) = ret_dest_ty
                                .expect("return destination and type not set");
                            base::store_ty(target, invokeret, ret_dest.llval, ret_ty);
                            build::Br(target, postinvoketarget.llbb, debugloc);
                        }
                    },
                    (_, &mir::CallKind::DivergingCleanup(_)) |
                    (_, &mir::CallKind::Diverging) => {
                        build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc);
                        build::Unreachable(bcx);
                    }
                    (_, k@&mir::CallKind::ConvergingCleanup { .. }) |
                    (_, k@&mir::CallKind::Converging { .. }) => {
                        // Bug #20046
                        let target = match *k {
                            mir::CallKind::ConvergingCleanup { targets, .. } => targets.0,
                            mir::CallKind::Converging { target, .. } => target,
                            _ => unreachable!()
                        };
                        let llret = build::Call(bcx,
                                                callee.immediate(),
                                                &llargs[..],
                                                Some(attrs),
                                                debugloc);
                        if must_copy_dest {
                            let (ret_dest, ret_ty) = ret_dest_ty
                                .expect("return destination and type not set");
                            base::store_ty(bcx, llret, ret_dest.llval, ret_ty);
                        }
                        build::Br(bcx, self.llblock(target), debugloc);
                    }
                }
            }
        }
    }

    fn get_personality_slot(&mut self, bcx: Block<'bcx, 'tcx>) -> ValueRef {
        let ccx = bcx.ccx();
        if let Some(slot) = self.llpersonalityslot {
            slot
        } else {
            let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
            let slot = base::alloca(bcx, llretty, "personalityslot");
            self.llpersonalityslot = Some(slot);
            base::call_lifetime_start(bcx, slot);
            slot
        }
    }

    fn make_landing_pad(&mut self, cleanup: Block<'bcx, 'tcx>) -> Block<'bcx, 'tcx> {
        let bcx = cleanup.fcx.new_block(true, "cleanup", None);
        let ccx = bcx.ccx();
        let llpersonality = bcx.fcx.eh_personality();
        let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
        let llretval = build::LandingPad(bcx, llretty, llpersonality, 1);
        build::SetCleanup(bcx, llretval);
        let slot = self.get_personality_slot(bcx);
        build::Store(bcx, llretval, slot);
        build::Br(bcx, cleanup.llbb, DebugLoc::None);
        bcx
    }

    fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> {
        match self.unreachable_block {
            Some(b) => b,
            None => {
                let bl = self.fcx.new_block(false, "unreachable", None);
                build::Unreachable(bl);
                self.unreachable_block = Some(bl);
                bl
            }
        }
    }

    fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> {
        self.blocks[bb.index()]
    }

    fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef {
        self.blocks[bb.index()].llbb
    }
}
<commit_msg>Fix type retrieval for Switch translation<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use llvm::{BasicBlockRef, ValueRef};
use rustc::mir::repr as mir;
use trans::adt;
use trans::base;
use trans::build;
use trans::attributes;
use trans::common::{self, Block};
use trans::debuginfo::DebugLoc;
use trans::type_of;
use trans::type_::Type;

use super::MirContext;
use super::operand::OperandValue::{FatPtr, Immediate, Ref};

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
    pub fn trans_block(&mut self, bb: mir::BasicBlock) {
        debug!("trans_block({:?})", bb);

        let mut bcx = self.bcx(bb);
        let data = self.mir.basic_block_data(bb);

        for statement in &data.statements {
            bcx = self.trans_statement(bcx, statement);
        }

        debug!("trans_block: terminator: {:?}", data.terminator());

        match *data.terminator() {
            mir::Terminator::Goto { target } => {
                build::Br(bcx, self.llblock(target), DebugLoc::None)
            }

            mir::Terminator::If { ref cond, targets: (true_bb, false_bb) } => {
                let cond = self.trans_operand(bcx, cond);
                let lltrue = self.llblock(true_bb);
                let llfalse = self.llblock(false_bb);
                build::CondBr(bcx, cond.immediate(), lltrue, llfalse, DebugLoc::None);
            }

            mir::Terminator::Switch { ref discr, ref adt_def, ref targets } => {
                let discr_lvalue = self.trans_lvalue(bcx, discr);
                let ty = discr_lvalue.ty.to_ty(bcx.tcx());
                let repr = adt::represent_type(bcx.ccx(), ty);
                let discr = adt::trans_get_discr(bcx, &repr, discr_lvalue.llval, None);

                // The else branch of the Switch can't be hit, so branch to an unreachable
                // instruction so LLVM knows that
                let unreachable_blk = self.unreachable_block();

                let switch = build::Switch(bcx, discr, unreachable_blk.llbb, targets.len());
                assert_eq!(adt_def.variants.len(), targets.len());
                for (adt_variant, target) in adt_def.variants.iter().zip(targets) {
                    let llval = adt::trans_case(bcx, &*repr, adt_variant.disr_val);
                    let llbb = self.llblock(*target);

                    build::AddCase(switch, llval, llbb)
                }
            }

            mir::Terminator::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
                let (otherwise, targets) = targets.split_last().unwrap();
                let discr = build::Load(bcx, self.trans_lvalue(bcx, discr).llval);
                let switch = build::Switch(bcx, discr, self.llblock(*otherwise), values.len());
                for (value, target) in values.iter().zip(targets) {
                    let llval = self.trans_constval(bcx, value, switch_ty).immediate();
                    let llbb = self.llblock(*target);
                    build::AddCase(switch, llval, llbb)
                }
            }

            mir::Terminator::Resume => {
                let ps = self.get_personality_slot(bcx);
                let lp = build::Load(bcx, ps);
                base::call_lifetime_end(bcx, ps);
                base::trans_unwind_resume(bcx, lp);
            }

            mir::Terminator::Return => {
                let return_ty = bcx.monomorphize(&self.mir.return_ty);
                base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None);
            }

            mir::Terminator::Call { ref func, ref args, ref kind } => {
                // Create the callee. This will always be a fn ptr and hence a kind of scalar.
                let callee = self.trans_operand(bcx, func);
                let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty);
                let debugloc = DebugLoc::None;
                // The arguments we'll be passing. Plus one to account for outptr, if used.
                let mut llargs = Vec::with_capacity(args.len() + 1);

                // Prepare the return value destination
                let (ret_dest_ty, must_copy_dest) = if let Some(d) = kind.destination() {
                    let dest = self.trans_lvalue(bcx, d);
                    let ret_ty = dest.ty.to_ty(bcx.tcx());
                    if type_of::return_uses_outptr(bcx.ccx(), ret_ty) {
                        llargs.push(dest.llval);
                        (Some((dest, ret_ty)), false)
                    } else {
                        (Some((dest, ret_ty)), !common::type_is_zero_size(bcx.ccx(), ret_ty))
                    }
                } else {
                    (None, false)
                };

                // Process the rest of the args.
                for arg in args {
                    match self.trans_operand(bcx, arg).val {
                        Ref(llval) | Immediate(llval) => llargs.push(llval),
                        FatPtr(b, e) => {
                            llargs.push(b);
                            llargs.push(e);
                        }
                    }
                }

                // Many different ways to call a function handled here
                match (base::avoid_invoke(bcx), kind) {
                    // The two cases below are the only ones to use LLVM’s `invoke`.
                    (false, &mir::CallKind::DivergingCleanup(cleanup)) => {
                        let cleanup = self.bcx(cleanup);
                        let landingpad = self.make_landing_pad(cleanup);
                        build::Invoke(bcx,
                                      callee.immediate(),
                                      &llargs[..],
                                      self.unreachable_block().llbb,
                                      landingpad.llbb,
                                      Some(attrs),
                                      debugloc);
                    },
                    (false, &mir::CallKind::ConvergingCleanup { ref targets, .. }) => {
                        let cleanup = self.bcx(targets.1);
                        let landingpad = self.make_landing_pad(cleanup);
                        let (target, postinvoke) = if must_copy_dest {
                            (bcx.fcx.new_block(false, "", None), Some(self.bcx(targets.0)))
                        } else {
                            (self.bcx(targets.0), None)
                        };
                        let invokeret = build::Invoke(bcx,
                                                      callee.immediate(),
                                                      &llargs[..],
                                                      target.llbb,
                                                      landingpad.llbb,
                                                      Some(attrs),
                                                      debugloc);
                        if let Some(postinvoketarget) = postinvoke {
                            // We translate the copy into a temoprary block. The temporary block is
                            // necessary because the current block has already been terminated (by
                            // `invoke`) and we cannot really translate into the target block
                            // because:
                            //  * The target block may have more than a single precedesor;
                            //  * Some LLVM insns cannot have a preceeding store insn (phi,
                            //    cleanuppad), and adding/prepending the store now may render
                            //    those other instructions invalid.
                            //
                            // NB: This approach still may break some LLVM code. For example if the
                            // target block starts with a `phi` (which may only match on immediate
                            // precedesors), it cannot know about this temporary block thus
                            // resulting in an invalid code:
                            //
                            // this:
                            //     …
                            //     %0 = …
                            //     %1 = invoke to label %temp …
                            // temp:
                            //     store ty %1, ty* %dest
                            //     br label %actualtargetblock
                            // actualtargetblock:            ; preds: %temp, …
                            //     phi … [%this, …], [%0, …] ; ERROR: phi requires to match only on
                            //                               ; immediate precedesors
                            let (ret_dest, ret_ty) = ret_dest_ty
                                .expect("return destination and type not set");
                            base::store_ty(target, invokeret, ret_dest.llval, ret_ty);
                            build::Br(target, postinvoketarget.llbb, debugloc);
                        }
                    },
                    (_, &mir::CallKind::DivergingCleanup(_)) |
                    (_, &mir::CallKind::Diverging) => {
                        build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc);
                        build::Unreachable(bcx);
                    }
                    (_, k@&mir::CallKind::ConvergingCleanup { .. }) |
                    (_, k@&mir::CallKind::Converging { .. }) => {
                        // Bug #20046
                        let target = match *k {
                            mir::CallKind::ConvergingCleanup { targets, .. } => targets.0,
                            mir::CallKind::Converging { target, .. } => target,
                            _ => unreachable!()
                        };
                        let llret = build::Call(bcx,
                                                callee.immediate(),
                                                &llargs[..],
                                                Some(attrs),
                                                debugloc);
                        if must_copy_dest {
                            let (ret_dest, ret_ty) = ret_dest_ty
                                .expect("return destination and type not set");
                            base::store_ty(bcx, llret, ret_dest.llval, ret_ty);
                        }
                        build::Br(bcx, self.llblock(target), debugloc);
                    }
                }
            }
        }
    }

    fn get_personality_slot(&mut self, bcx: Block<'bcx, 'tcx>) -> ValueRef {
        let ccx = bcx.ccx();
        if let Some(slot) = self.llpersonalityslot {
            slot
        } else {
            let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
            let slot = base::alloca(bcx, llretty, "personalityslot");
            self.llpersonalityslot = Some(slot);
            base::call_lifetime_start(bcx, slot);
            slot
        }
    }

    fn make_landing_pad(&mut self, cleanup: Block<'bcx, 'tcx>) -> Block<'bcx, 'tcx> {
        let bcx = cleanup.fcx.new_block(true, "cleanup", None);
        let ccx = bcx.ccx();
        let llpersonality = bcx.fcx.eh_personality();
        let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
        let llretval = build::LandingPad(bcx, llretty, llpersonality, 1);
        build::SetCleanup(bcx, llretval);
        let slot = self.get_personality_slot(bcx);
        build::Store(bcx, llretval, slot);
        build::Br(bcx, cleanup.llbb, DebugLoc::None);
        bcx
    }

    fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> {
        match self.unreachable_block {
            Some(b) => b,
            None => {
                let bl = self.fcx.new_block(false, "unreachable", None);
                build::Unreachable(bl);
                self.unreachable_block = Some(bl);
                bl
            }
        }
    }

    fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> {
        self.blocks[bb.index()]
    }

    fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef {
        self.blocks[bb.index()].llbb
    }
}
<|endoftext|>