<commit_before>use std::fmt::{self, Debug, Formatter};

use url::Url;

use core::source::{Source, SourceId};
use core::GitReference;
use core::{Dependency, Package, PackageId, Summary};
use util::Config;
use util::errors::CargoResult;
use util::hex::short_hash;
use sources::PathSource;
use sources::git::utils::{GitRemote, GitRevision};

/* TODO: Refactor GitSource to delegate to a PathSource
 */
pub struct GitSource<'cfg> {
    remote: GitRemote,
    reference: GitReference,
    source_id: SourceId,
    path_source: Option<PathSource<'cfg>>,
    rev: Option<GitRevision>,
    ident: String,
    config: &'cfg Config,
}

impl<'cfg> GitSource<'cfg> {
    pub fn new(source_id: &SourceId, config: &'cfg Config) -> CargoResult<GitSource<'cfg>> {
        assert!(source_id.is_git(), "id is not git, id={}", source_id);

        let remote = GitRemote::new(source_id.url());
        let ident = ident(source_id.url())?;

        let reference = match source_id.precise() {
            Some(s) => GitReference::Rev(s.to_string()),
            None => source_id.git_reference().unwrap().clone(),
        };

        let source = GitSource {
            remote,
            reference,
            source_id: source_id.clone(),
            path_source: None,
            rev: None,
            ident,
            config,
        };

        Ok(source)
    }

    pub fn url(&self) -> &Url {
        self.remote.url()
    }

    pub fn read_packages(&mut self) -> CargoResult<Vec<Package>> {
        if self.path_source.is_none() {
            self.update()?;
        }
        self.path_source.as_mut().unwrap().read_packages()
    }
}

fn ident(url: &Url) -> CargoResult<String> {
    let url = canonicalize_url(url)?;
    let ident = url.path_segments()
        .and_then(|mut s| s.next_back())
        .unwrap_or("");

    let ident = if ident == "" { "_empty" } else { ident };

    Ok(format!("{}-{}", ident, short_hash(&url)))
}

// Some hacks and heuristics for making equivalent URLs hash the same
pub fn canonicalize_url(url: &Url) -> CargoResult<Url> {
    let mut url = url.clone();

    // cannot-be-a-base-urls are not supported
    // eg. github.com:rust-lang-nursery/rustfmt.git
    if url.cannot_be_a_base() {
        bail!(
            "invalid url `{}`: cannot-be-a-base-URLs are not supported",
            url
        )
    }

    // Strip a trailing slash
    if url.path().ends_with('/') {
        url.path_segments_mut().unwrap().pop_if_empty();
    }

    // HACKHACK: For GitHub URL's specifically just lowercase
    // everything.  GitHub treats both the same, but they hash
    // differently, and we're gonna be hashing them. This wants a more
    // general solution, and also we're almost certainly not using the
    // same case conversion rules that GitHub does. (#84)
    if url.host_str() == Some("github.com") {
        url.set_scheme("https").unwrap();
        let path = url.path().to_lowercase();
        url.set_path(&path);
    }

    // Repos generally can be accessed with or w/o '.git'
    let needs_chopping = url.path().ends_with(".git");
    if needs_chopping {
        let last = {
            let last = url.path_segments().unwrap().next_back().unwrap();
            last[..last.len() - 4].to_owned()
        };
        url.path_segments_mut().unwrap().pop().push(&last);
    }

    Ok(url)
}

impl<'cfg> Debug for GitSource<'cfg> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "git repo at {}", self.remote.url())?;

        match self.reference.pretty_ref() {
            Some(s) => write!(f, " ({})", s),
            None => Ok(()),
        }
    }
}

impl<'cfg> Source for GitSource<'cfg> {
    fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        let src = self.path_source
            .as_mut()
            .expect("BUG: update() must be called before query()");
        src.query(dep, f)
    }

    fn supports_checksums(&self) -> bool {
        false
    }

    fn requires_precise(&self) -> bool {
        true
    }

    fn source_id(&self) -> &SourceId {
        &self.source_id
    }

    fn update(&mut self) -> CargoResult<()> {
        let lock =
            self.config
                .git_path()
                .open_rw(".cargo-lock-git", self.config, "the git checkouts")?;

        let db_path = lock.parent().join("db").join(&self.ident);

        if self.config.cli_unstable().offline && !db_path.exists() {
            bail!(
                "can't checkout from '{}': you are in the offline mode (-Z offline)",
                self.remote.url()
            );
        }

        // Resolve our reference to an actual revision, and check if the
        // database already has that revision. If it does, we just load a
        // database pinned at that revision, and if we don't we issue an update
        // to try to find the revision.
        let actual_rev = self.remote.rev_for(&db_path, &self.reference);
        let should_update = actual_rev.is_err() || self.source_id.precise().is_none();

        let (db, actual_rev) = if should_update && !self.config.cli_unstable().offline {
            self.config.shell().status(
                "Updating",
                format!("git repository `{}`", self.remote.url()),
            )?;

            trace!("updating git source `{:?}`", self.remote);

            self.remote
                .checkout(&db_path, &self.reference, self.config)?
        } else {
            (self.remote.db_at(&db_path)?, actual_rev.unwrap())
        };

        // Don’t use the full hash,
        // to contribute less to reaching the path length limit on Windows:
        // https://github.com/servo/servo/pull/14397
        let short_id = db.to_short_id(actual_rev.clone()).unwrap();

        let checkout_path = lock.parent()
            .join("checkouts")
            .join(&self.ident)
            .join(short_id.as_str());

        // Copy the database to the checkout location. After this we could drop
        // the lock on the database as we no longer needed it, but we leave it
        // in scope so the destructors here won't tamper with too much.
        // Checkout is immutable, so we don't need to protect it with a lock once
        // it is created.
        db.copy_to(actual_rev.clone(), &checkout_path, self.config)?;

        let source_id = self.source_id.with_precise(Some(actual_rev.to_string()));
        let path_source = PathSource::new_recursive(&checkout_path, &source_id, self.config);

        self.path_source = Some(path_source);
        self.rev = Some(actual_rev);
        self.path_source.as_mut().unwrap().update()
    }

    fn download(&mut self, id: &PackageId) -> CargoResult<Package> {
        trace!(
            "getting packages for package id `{}` from `{:?}`",
            id,
            self.remote
        );
        self.path_source
            .as_mut()
            .expect("BUG: update() must be called before get()")
            .download(id)
    }

    fn fingerprint(&self, _pkg: &Package) -> CargoResult<String> {
        Ok(self.rev.as_ref().unwrap().to_string())
    }
}

#[cfg(test)]
mod test {
    use url::Url;
    use super::ident;
    use util::ToUrl;

    #[test]
    pub fn test_url_to_path_ident_with_path() {
        let ident = ident(&url("https://github.com/carlhuda/cargo")).unwrap();
        assert!(ident.starts_with("cargo-"));
    }

    #[test]
    pub fn test_url_to_path_ident_without_path() {
        let ident = ident(&url("https://github.com")).unwrap();
        assert!(ident.starts_with("_empty-"));
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_trailing_url_slash() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston/")).unwrap();
        let ident2 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_lowercasing_github_urls() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("https://github.com/pistondevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_dot_git() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("https://github.com/PistonDevelopers/piston.git")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_different_protocols() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("git://github.com/PistonDevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_cannot_be_a_base_urls() {
        assert!(ident(&url("github.com:PistonDevelopers/piston")).is_err());
        assert!(ident(&url("google.com:PistonDevelopers/piston")).is_err());
    }

    fn url(s: &str) -> Url {
        s.to_url().unwrap()
    }
}
<commit_msg>Drop PathSource TODO in GitSource<commit_after>use std::fmt::{self, Debug, Formatter};

use url::Url;

use core::source::{Source, SourceId};
use core::GitReference;
use core::{Dependency, Package, PackageId, Summary};
use util::Config;
use util::errors::CargoResult;
use util::hex::short_hash;
use sources::PathSource;
use sources::git::utils::{GitRemote, GitRevision};

pub struct GitSource<'cfg> {
    remote: GitRemote,
    reference: GitReference,
    source_id: SourceId,
    path_source: Option<PathSource<'cfg>>,
    rev: Option<GitRevision>,
    ident: String,
    config: &'cfg Config,
}

impl<'cfg> GitSource<'cfg> {
    pub fn new(source_id: &SourceId, config: &'cfg Config) -> CargoResult<GitSource<'cfg>> {
        assert!(source_id.is_git(), "id is not git, id={}", source_id);

        let remote = GitRemote::new(source_id.url());
        let ident = ident(source_id.url())?;

        let reference = match source_id.precise() {
            Some(s) => GitReference::Rev(s.to_string()),
            None => source_id.git_reference().unwrap().clone(),
        };

        let source = GitSource {
            remote,
            reference,
            source_id: source_id.clone(),
            path_source: None,
            rev: None,
            ident,
            config,
        };

        Ok(source)
    }

    pub fn url(&self) -> &Url {
        self.remote.url()
    }

    pub fn read_packages(&mut self) -> CargoResult<Vec<Package>> {
        if self.path_source.is_none() {
            self.update()?;
        }
        self.path_source.as_mut().unwrap().read_packages()
    }
}

fn ident(url: &Url) -> CargoResult<String> {
    let url = canonicalize_url(url)?;
    let ident = url.path_segments()
        .and_then(|mut s| s.next_back())
        .unwrap_or("");

    let ident = if ident == "" { "_empty" } else { ident };

    Ok(format!("{}-{}", ident, short_hash(&url)))
}

// Some hacks and heuristics for making equivalent URLs hash the same
pub fn canonicalize_url(url: &Url) -> CargoResult<Url> {
    let mut url = url.clone();

    // cannot-be-a-base-urls are not supported
    // eg. github.com:rust-lang-nursery/rustfmt.git
    if url.cannot_be_a_base() {
        bail!(
            "invalid url `{}`: cannot-be-a-base-URLs are not supported",
            url
        )
    }

    // Strip a trailing slash
    if url.path().ends_with('/') {
        url.path_segments_mut().unwrap().pop_if_empty();
    }

    // HACKHACK: For GitHub URL's specifically just lowercase
    // everything.  GitHub treats both the same, but they hash
    // differently, and we're gonna be hashing them. This wants a more
    // general solution, and also we're almost certainly not using the
    // same case conversion rules that GitHub does. (#84)
    if url.host_str() == Some("github.com") {
        url.set_scheme("https").unwrap();
        let path = url.path().to_lowercase();
        url.set_path(&path);
    }

    // Repos generally can be accessed with or w/o '.git'
    let needs_chopping = url.path().ends_with(".git");
    if needs_chopping {
        let last = {
            let last = url.path_segments().unwrap().next_back().unwrap();
            last[..last.len() - 4].to_owned()
        };
        url.path_segments_mut().unwrap().pop().push(&last);
    }

    Ok(url)
}

impl<'cfg> Debug for GitSource<'cfg> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "git repo at {}", self.remote.url())?;

        match self.reference.pretty_ref() {
            Some(s) => write!(f, " ({})", s),
            None => Ok(()),
        }
    }
}

impl<'cfg> Source for GitSource<'cfg> {
    fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        let src = self.path_source
            .as_mut()
            .expect("BUG: update() must be called before query()");
        src.query(dep, f)
    }

    fn supports_checksums(&self) -> bool {
        false
    }

    fn requires_precise(&self) -> bool {
        true
    }

    fn source_id(&self) -> &SourceId {
        &self.source_id
    }

    fn update(&mut self) -> CargoResult<()> {
        let lock =
            self.config
                .git_path()
                .open_rw(".cargo-lock-git", self.config, "the git checkouts")?;

        let db_path = lock.parent().join("db").join(&self.ident);

        if self.config.cli_unstable().offline && !db_path.exists() {
            bail!(
                "can't checkout from '{}': you are in the offline mode (-Z offline)",
                self.remote.url()
            );
        }

        // Resolve our reference to an actual revision, and check if the
        // database already has that revision. If it does, we just load a
        // database pinned at that revision, and if we don't we issue an update
        // to try to find the revision.
        let actual_rev = self.remote.rev_for(&db_path, &self.reference);
        let should_update = actual_rev.is_err() || self.source_id.precise().is_none();

        let (db, actual_rev) = if should_update && !self.config.cli_unstable().offline {
            self.config.shell().status(
                "Updating",
                format!("git repository `{}`", self.remote.url()),
            )?;

            trace!("updating git source `{:?}`", self.remote);

            self.remote
                .checkout(&db_path, &self.reference, self.config)?
        } else {
            (self.remote.db_at(&db_path)?, actual_rev.unwrap())
        };

        // Don’t use the full hash,
        // to contribute less to reaching the path length limit on Windows:
        // https://github.com/servo/servo/pull/14397
        let short_id = db.to_short_id(actual_rev.clone()).unwrap();

        let checkout_path = lock.parent()
            .join("checkouts")
            .join(&self.ident)
            .join(short_id.as_str());

        // Copy the database to the checkout location. After this we could drop
        // the lock on the database as we no longer needed it, but we leave it
        // in scope so the destructors here won't tamper with too much.
        // Checkout is immutable, so we don't need to protect it with a lock once
        // it is created.
        db.copy_to(actual_rev.clone(), &checkout_path, self.config)?;

        let source_id = self.source_id.with_precise(Some(actual_rev.to_string()));
        let path_source = PathSource::new_recursive(&checkout_path, &source_id, self.config);

        self.path_source = Some(path_source);
        self.rev = Some(actual_rev);
        self.path_source.as_mut().unwrap().update()
    }

    fn download(&mut self, id: &PackageId) -> CargoResult<Package> {
        trace!(
            "getting packages for package id `{}` from `{:?}`",
            id,
            self.remote
        );
        self.path_source
            .as_mut()
            .expect("BUG: update() must be called before get()")
            .download(id)
    }

    fn fingerprint(&self, _pkg: &Package) -> CargoResult<String> {
        Ok(self.rev.as_ref().unwrap().to_string())
    }
}

#[cfg(test)]
mod test {
    use url::Url;
    use super::ident;
    use util::ToUrl;

    #[test]
    pub fn test_url_to_path_ident_with_path() {
        let ident = ident(&url("https://github.com/carlhuda/cargo")).unwrap();
        assert!(ident.starts_with("cargo-"));
    }

    #[test]
    pub fn test_url_to_path_ident_without_path() {
        let ident = ident(&url("https://github.com")).unwrap();
        assert!(ident.starts_with("_empty-"));
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_trailing_url_slash() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston/")).unwrap();
        let ident2 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_lowercasing_github_urls() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("https://github.com/pistondevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_dot_git() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("https://github.com/PistonDevelopers/piston.git")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_different_protocols() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("git://github.com/PistonDevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_cannot_be_a_base_urls() {
        assert!(ident(&url("github.com:PistonDevelopers/piston")).is_err());
        assert!(ident(&url("google.com:PistonDevelopers/piston")).is_err());
    }

    fn url(s: &str) -> Url {
        s.to_url().unwrap()
    }
}
<|endoftext|>


<commit_before>use cpp_ffi_data::CppAndFfiMethod;
use cpp_ffi_data::IndirectionChange;
use cpp_method::ReturnValueAllocationPlace;
use cpp_ffi_data::CppFfiArgumentMeaning;
use cpp_ffi_generator::CppFfiHeaderData;
use log;
use string_utils::JoinWithString;
use std::path::PathBuf;
use file_utils::{PathBufWithAdded, create_dir_all, create_file};
use utils::is_msvc;
use cpp_type::{CppTypeIndirection, CppTypeBase};
use errors::{Result, ChainErr, unexpected};

/// Generates C++ code for the C wrapper library.
pub struct CppCodeGenerator {
  /// Library name
  lib_name: String,
  /// Uppercase library name (for optimization)
  lib_name_upper: String,
  /// Path to the directory where the library is generated
  lib_path: PathBuf,

  is_shared: bool,
  cpp_libs: Vec<String>,
}

impl CppCodeGenerator {
  /// Creates a generator for a library.
  /// lib_name: library name
  /// lib_path: path to the directory where the library is generated
  pub fn new(lib_name: String, lib_path: PathBuf, is_shared: bool, cpp_libs: Vec<String>) -> Self {
    CppCodeGenerator {
      lib_name: lib_name.clone(),
      lib_name_upper: lib_name.to_uppercase(),
      lib_path: lib_path,
      is_shared: is_shared,
      cpp_libs: cpp_libs,
    }
  }

  /// Generates function name, return type and arguments list
  /// as it appears in both function declaration and implementation.
  fn function_signature(&self, method: &CppAndFfiMethod) -> Result<String> {
    let mut arg_texts = Vec::new();
    for arg in &method.c_signature.arguments {
      arg_texts.push(try!(arg.to_cpp_code()));
    }
    let name_with_args = format!("{}({})", method.c_name, arg_texts.join(", "));
    let return_type = &method.c_signature.return_type.ffi_type;
    let r = if let CppTypeBase::FunctionPointer { .. } = return_type.base {
      try!(return_type.to_cpp_code(Some(&name_with_args)))
    } else {
      format!("{} {}", try!(return_type.to_cpp_code(None)), name_with_args)
    };
    Ok(r)
  }

  /// Generates method declaration for the header.
  fn function_declaration(&self, method: &CppAndFfiMethod) -> Result<String> {
    Ok(format!("{}_EXPORT {};\n",
               self.lib_name_upper,
               try!(self.function_signature(method))))
  }

  /// Wraps expression returned by the original method to
  /// convert it to return type of the FFI method.
  fn convert_return_type(&self, method: &CppAndFfiMethod, expression: String) -> Result<String> {
    let mut result = expression;
    match method.c_signature.return_type.conversion {
      IndirectionChange::NoChange => {}
      IndirectionChange::ValueToPointer => {
        match method.allocation_place {
          ReturnValueAllocationPlace::Stack => {
            return Err(unexpected("stack allocated wrappers are expected to return void"));
          }
          ReturnValueAllocationPlace::NotApplicable => {
            return Err(unexpected("ValueToPointer conflicts with NotApplicable"));
          }
          ReturnValueAllocationPlace::Heap => {
            // constructors are said to return values in parse result,
            // but in reality we use `new` which returns a pointer,
            // so no conversion is necessary for constructors.
            if !method.cpp_method.is_constructor() {
              result = format!("new {}({})",
                               try!(method.cpp_method.return_type.base.to_cpp_code(None)),
                               result);
            }
          }
        }
      }
      IndirectionChange::ReferenceToPointer => {
        result = format!("&{}", result);
      }
      IndirectionChange::QFlagsToUInt => {
        result = format!("uint({})", result);
      }
    }

    if method.allocation_place == ReturnValueAllocationPlace::Stack &&
       !method.cpp_method.is_constructor() {
      if let Some(arg) = method.c_signature
        .arguments
        .iter()
        .find(|x| x.meaning == CppFfiArgumentMeaning::ReturnValue) {
        result = format!("new({}) {}({})",
                         arg.name,
                         try!(method.cpp_method.return_type.base.to_cpp_code(None)),
                         result);
      }
    }
    Ok(result)
  }

  /// Generates code for values passed to the original C++ method.
  fn arguments_values(&self, method: &CppAndFfiMethod) -> Result<String> {
    let mut filled_arguments = vec![];
    for (i, cpp_argument) in method.cpp_method.arguments.iter().enumerate() {
      if let Some(c_argument) = method.c_signature
        .arguments
        .iter()
        .find(|x| x.meaning == CppFfiArgumentMeaning::Argument(i as i8)) {
        let mut result = c_argument.name.clone();
        match c_argument.argument_type
          .conversion {
          IndirectionChange::ValueToPointer |
          IndirectionChange::ReferenceToPointer => result = format!("*{}", result),
          IndirectionChange::NoChange => {}
          IndirectionChange::QFlagsToUInt => {
            let type_text = if cpp_argument.argument_type.indirection == CppTypeIndirection::Ref &&
                               cpp_argument.argument_type.is_const {
              let mut fake_type = cpp_argument.argument_type.clone();
              fake_type.is_const = false;
              fake_type.indirection = CppTypeIndirection::None;
              try!(fake_type.to_cpp_code(None))
            } else {
              try!(cpp_argument.argument_type.to_cpp_code(None))
            };
            result = format!("{}({})", type_text, result);
          }
        }
        filled_arguments.push(result);
      } else {
        panic!("Error: no positional argument found\n{:?}", method);
      }
    }
    Ok(filled_arguments.into_iter().join(", "))
  }

  /// Generates code for the value returned by the FFI method.
  #[cfg_attr(feature="clippy", allow(collapsible_if))]
  fn returned_expression(&self, method: &CppAndFfiMethod) -> Result<String> {
    let result = if method.cpp_method.is_destructor() {
      if let Some(arg) = method.c_signature
        .arguments
        .iter()
        .find(|x| x.meaning == CppFfiArgumentMeaning::This) {
        format!("{}_call_destructor({})", self.lib_name, arg.name)
      } else {
        return Err(unexpected("no this arg in destructor"));
      }
    } else {
      let result_without_args = if method.cpp_method.is_constructor() {
        if let Some(ref info) = method.cpp_method.class_membership {
          let class_type = &info.class_type;
          match method.allocation_place {
            ReturnValueAllocationPlace::Stack => {
              if let Some(arg) = method.c_signature
                .arguments
                .iter()
                .find(|x| x.meaning == CppFfiArgumentMeaning::ReturnValue) {
                format!("new({}) {}", arg.name, try!(class_type.to_cpp_code()))
              } else {
                return Err(unexpected("return value argument not found"));
              }
            }
            ReturnValueAllocationPlace::Heap => format!("new {}", try!(class_type.to_cpp_code())),
            ReturnValueAllocationPlace::NotApplicable => {
              return Err(unexpected("NotApplicable in constructor"));
            }
          }
        } else {
          return Err(unexpected("constructor without class membership").into());
        }
      } else {
        let scope_specifier = if let Some(ref class_membership) = method.cpp_method
          .class_membership {
          if class_membership.is_static {
            format!("{}::", try!(class_membership.class_type.to_cpp_code()))
          } else {
            if let Some(arg) = method.c_signature
              .arguments
              .iter()
              .find(|x| x.meaning == CppFfiArgumentMeaning::This) {
              format!("{}->", arg.name)
            } else {
              return Err(unexpected("no this arg in non-static method"));
            }
          }
        } else {
          "".to_string()
        };
        let template_args = match method.cpp_method.template_arguments_values {
          Some(ref args) => {
            let mut texts = Vec::new();
            for arg in args {
              texts.push(try!(arg.to_cpp_code(None)));
            }
            format!("<{}>", texts.join(", "))
          }
          None => String::new(),
        };
        format!("{}{}{}",
                scope_specifier,
                method.cpp_method.name,
                template_args)
      };
      format!("{}({})",
              result_without_args,
              try!(self.arguments_values(method)))
    };
    self.convert_return_type(method, result)
  }

  /// Generates body of the FFI method implementation.
  fn source_body(&self, method: &CppAndFfiMethod) -> Result<String> {
    if method.cpp_method.is_destructor() &&
       method.allocation_place == ReturnValueAllocationPlace::Heap {
      if let Some(arg) = method.c_signature
        .arguments
        .iter()
        .find(|x| x.meaning == CppFfiArgumentMeaning::This) {
        Ok(format!("delete {};\n", arg.name))
      } else {
        panic!("Error: no this argument found\n{:?}", method);
      }
    } else {
      Ok(format!("{}{};\n",
                 if method.c_signature.return_type.ffi_type.is_void() {
                   ""
                 } else {
                   "return "
                 },
                 try!(self.returned_expression(&method))))
    }
  }

  /// Generates implementation of the FFI method for the source file.
  fn function_implementation(&self, method: &CppAndFfiMethod) -> Result<String> {
    Ok(format!("{} {{\n  {}}}\n\n",
               try!(self.function_signature(method)),
               try!(self.source_body(&method))))
  }

  /// Generates main files and directories of the library.
  pub fn generate_template_files(&self,
                                 cpp_lib_include_file: &str,
                                 include_directories: &[String],
                                 framework_directories: &[String])
                                 -> Result<()> {
    let name_upper = self.lib_name.to_uppercase();
    let cmakelists_path = self.lib_path.with_added("CMakeLists.txt");
    let mut cmakelists_file = try!(create_file(&cmakelists_path));
    let mut cxx_flags = String::new();
    if !is_msvc() {
      cxx_flags.push_str("-fPIC -std=gnu++11");
    }
    for dir in framework_directories {
      cxx_flags.push_str(&format!(" -F\\\"{}\\\"", dir));
    }
    try!(cmakelists_file.write(format!(include_str!("../templates/c_lib/CMakeLists.txt"),
                                       lib_name_lowercase = &self.lib_name,
                                       lib_name_uppercase = name_upper,
                                       include_directories = include_directories.into_iter()
                                         .map(|x| format!("\"{}\"", x.replace(r"\", r"\\")))
                                         .join(" "),
                                       library_type =
                                         if self.is_shared { "SHARED" } else { "STATIC" },
                                       target_link_libraries = if self.is_shared {
                                         format!("target_link_libraries({} {})",
                                                 &self.lib_name,
                                                 self.cpp_libs.join(" "))
                                       } else {
                                         String::new()
                                       },
                                       cxx_flags = cxx_flags)));
    let src_dir = self.lib_path.with_added("src");
    try!(create_dir_all(&src_dir));

    let include_dir = self.lib_path.with_added("include");
    try!(create_dir_all(&include_dir));
    let exports_file_path = include_dir.with_added(format!("{}_exports.h", &self.lib_name));
    let mut exports_file = try!(create_file(&exports_file_path));
    try!(exports_file.write(format!(include_str!("../templates/c_lib/exports.h"),
                                    lib_name_uppercase = name_upper)));

    let global_file_path = include_dir.with_added(format!("{}_global.h", &self.lib_name));
    let mut global_file = try!(create_file(&global_file_path));
    try!(global_file.write(format!(include_str!("../templates/c_lib/global.h"),
                                   lib_name_lowercase = &self.lib_name,
                                   lib_name_uppercase = name_upper,
                                   cpp_lib_include_file = cpp_lib_include_file)));
    Ok(())
  }

  pub fn generate_files(&self, data: &[CppFfiHeaderData]) -> Result<()> {
    try!(self.generate_all_headers_file(data.iter().map(|x| &x.include_file)));
    for item in data {
      try!(self.generate_one(item).chain_err(|| "C++ code generator failed"));
    }
    Ok(())
  }

  /// Generates the header file that includes all other headers of the library.
  fn generate_all_headers_file<'a, I: Iterator<Item = &'a String>>(&self, names: I) -> Result<()> {
    let mut h_path = self.lib_path.clone();
    h_path.push("include");
    h_path.push(format!("{}.h", &self.lib_name));
    let mut all_header_file = try!(create_file(&h_path));
    try!(all_header_file.write(format!("#ifndef {0}_H\n#define {0}_H\n\n", &self.lib_name_upper)));
    for name in names {
      try!(all_header_file.write(format!("#include \"{}_{}.h\"\n", &self.lib_name, name)));
    }
    try!(all_header_file.write(format!("#endif // {}_H\n", &self.lib_name_upper)));
    Ok(())
  }

  /// Generates a header file and a source file for a portion of data
  /// corresponding to a header file of original C++ library.
  fn generate_one(&self, data: &CppFfiHeaderData) -> Result<()> {
    let ffi_include_file = format!("{}_{}.h", &self.lib_name, data.include_file_base_name);

    let cpp_path = self.lib_path
      .with_added("src")
      .with_added(format!("{}_{}.cpp", &self.lib_name, data.include_file_base_name));
    log::noisy(format!("Generating source file: {:?}", cpp_path));

    let h_path = self.lib_path.with_added("include").with_added(&ffi_include_file);
    log::noisy(format!("Generating header file: {:?}", h_path));

    let mut cpp_file = try!(create_file(&cpp_path));
    let mut h_file = try!(create_file(&h_path));

    try!(cpp_file.write(format!("#include \"{}\"\n\n", ffi_include_file)));
    let include_guard_name = ffi_include_file.replace(".", "_").to_uppercase();
    try!(h_file.write(format!("#ifndef {}\n#define {}\n\n",
                              include_guard_name,
                              include_guard_name)));

    try!(h_file.write(format!("#include \"{}_global.h\"\n\n", &self.lib_name)));

    try!(h_file.write("extern \"C\" {\n\n"));

    for method in &data.methods {
      try!(h_file.write(try!(self.function_declaration(method))));
      try!(cpp_file.write(try!(self.function_implementation(method))));
    }

    try!(h_file.write("\n} // extern \"C\"\n\n"));

    try!(h_file.write(format!("#endif // {}\n", include_guard_name)));
    Ok(())
  }
}
<commit_msg>small reuse of new function<commit_after>use cpp_ffi_data::CppAndFfiMethod;
use cpp_ffi_data::IndirectionChange;
use cpp_method::ReturnValueAllocationPlace;
use cpp_ffi_data::CppFfiArgumentMeaning;
use cpp_ffi_generator::CppFfiHeaderData;
use log;
use string_utils::JoinWithString;
use std::path::PathBuf;
use file_utils::{PathBufWithAdded, create_dir_all, create_file};
use utils::is_msvc;
use cpp_type::{CppTypeIndirection, CppTypeBase};
use errors::{Result, ChainErr, unexpected};

/// Generates C++ code for the C wrapper library.
pub struct CppCodeGenerator {
  /// Library name
  lib_name: String,
  /// Uppercase library name (for optimization)
  lib_name_upper: String,
  /// Path to the directory where the library is generated
  lib_path: PathBuf,

  is_shared: bool,
  cpp_libs: Vec<String>,
}

impl CppCodeGenerator {
  /// Creates a generator for a library.
  /// lib_name: library name
  /// lib_path: path to the directory where the library is generated
  pub fn new(lib_name: String, lib_path: PathBuf, is_shared: bool, cpp_libs: Vec<String>) -> Self {
    CppCodeGenerator {
      lib_name: lib_name.clone(),
      lib_name_upper: lib_name.to_uppercase(),
      lib_path: lib_path,
      is_shared: is_shared,
      cpp_libs: cpp_libs,
    }
  }

  /// Generates function name, return type and arguments list
  /// as it appears in both function declaration and implementation.
  fn function_signature(&self, method: &CppAndFfiMethod) -> Result<String> {
    let mut arg_texts = Vec::new();
    for arg in &method.c_signature.arguments {
      arg_texts.push(try!(arg.to_cpp_code()));
    }
    let name_with_args = format!("{}({})", method.c_name, arg_texts.join(", "));
    let return_type = &method.c_signature.return_type.ffi_type;
    let r = if let CppTypeBase::FunctionPointer { .. } = return_type.base {
      try!(return_type.to_cpp_code(Some(&name_with_args)))
    } else {
      format!("{} {}", try!(return_type.to_cpp_code(None)), name_with_args)
    };
    Ok(r)
  }

  /// Generates method declaration for the header.
  fn function_declaration(&self, method: &CppAndFfiMethod) -> Result<String> {
    Ok(format!("{}_EXPORT {};\n",
               self.lib_name_upper,
               try!(self.function_signature(method))))
  }

  /// Wraps expression returned by the original method to
  /// convert it to return type of the FFI method.
  fn convert_return_type(&self, method: &CppAndFfiMethod, expression: String) -> Result<String> {
    let mut result = expression;
    match method.c_signature.return_type.conversion {
      IndirectionChange::NoChange => {}
      IndirectionChange::ValueToPointer => {
        match method.allocation_place {
          ReturnValueAllocationPlace::Stack => {
            return Err(unexpected("stack allocated wrappers are expected to return void"));
          }
          ReturnValueAllocationPlace::NotApplicable => {
            return Err(unexpected("ValueToPointer conflicts with NotApplicable"));
          }
          ReturnValueAllocationPlace::Heap => {
            // constructors are said to return values in parse result,
            // but in reality we use `new` which returns a pointer,
            // so no conversion is necessary for constructors.
            if !method.cpp_method.is_constructor() {
              result = format!("new {}({})",
                               try!(method.cpp_method.return_type.base.to_cpp_code(None)),
                               result);
            }
          }
        }
      }
      IndirectionChange::ReferenceToPointer => {
        result = format!("&{}", result);
      }
      IndirectionChange::QFlagsToUInt => {
        result = format!("uint({})", result);
      }
    }

    if method.allocation_place == ReturnValueAllocationPlace::Stack &&
       !method.cpp_method.is_constructor() {
      if let Some(arg) = method.c_signature
        .arguments
        .iter()
        .find(|x| x.meaning == CppFfiArgumentMeaning::ReturnValue) {
        result = format!("new({}) {}({})",
                         arg.name,
                         try!(method.cpp_method.return_type.base.to_cpp_code(None)),
                         result);
      }
    }
    Ok(result)
  }

  /// Generates code for values passed to the original C++ method.
  fn arguments_values(&self, method: &CppAndFfiMethod) -> Result<String> {
    let mut filled_arguments = vec![];
    for (i, cpp_argument) in method.cpp_method.arguments.iter().enumerate() {
      if let Some(c_argument) = method.c_signature
        .arguments
        .iter()
        .find(|x| x.meaning == CppFfiArgumentMeaning::Argument(i as i8)) {
        let mut result = c_argument.name.clone();
        match c_argument.argument_type
          .conversion {
          IndirectionChange::ValueToPointer |
          IndirectionChange::ReferenceToPointer => result = format!("*{}", result),
          IndirectionChange::NoChange => {}
          IndirectionChange::QFlagsToUInt => {
            let type_text = if cpp_argument.argument_type.indirection == CppTypeIndirection::Ref &&
                               cpp_argument.argument_type.is_const {
              let mut fake_type = cpp_argument.argument_type.clone();
              fake_type.is_const = false;
              fake_type.indirection = CppTypeIndirection::None;
              try!(fake_type.to_cpp_code(None))
            } else {
              try!(cpp_argument.argument_type.to_cpp_code(None))
            };
            result = format!("{}({})", type_text, result);
          }
        }
        filled_arguments.push(result);
      } else {
        panic!("Error: no positional argument found\n{:?}", method);
      }
    }
    Ok(filled_arguments.into_iter().join(", "))
  }

  /// Generates code for the value returned by the FFI method.
  #[cfg_attr(feature="clippy", allow(collapsible_if))]
  fn returned_expression(&self, method: &CppAndFfiMethod) -> Result<String> {
    let result = if method.cpp_method.is_destructor() {
      if let Some(arg) = method.c_signature
        .arguments
        .iter()
        .find(|x| x.meaning == CppFfiArgumentMeaning::This) {
        format!("{}_call_destructor({})", self.lib_name, arg.name)
      } else {
        return Err(unexpected("no this arg in destructor"));
      }
    } else {
      let result_without_args = if let Some(ref info) = method.cpp_method.class_info_if_constructor() {
        let class_type = &info.class_type;
        match method.allocation_place {
          ReturnValueAllocationPlace::Stack => {
            if let Some(arg) = method.c_signature
              .arguments
              .iter()
              .find(|x| x.meaning == CppFfiArgumentMeaning::ReturnValue) {
              format!("new({}) {}", arg.name, try!(class_type.to_cpp_code()))
            } else {
              return Err(unexpected(format!("return value argument not found\n{:?}", method)));
            }
          }
          ReturnValueAllocationPlace::Heap => format!("new {}", try!(class_type.to_cpp_code())),
          ReturnValueAllocationPlace::NotApplicable => {
            return Err(unexpected("NotApplicable in constructor"));
          }
        }
      } else {
        let scope_specifier = if let Some(ref class_membership) = method.cpp_method
          .class_membership {
          if class_membership.is_static {
            format!("{}::", try!(class_membership.class_type.to_cpp_code()))
          } else {
            if let Some(arg) = method.c_signature
              .arguments
              .iter()
              .find(|x| x.meaning == CppFfiArgumentMeaning::This) {
              format!("{}->", arg.name)
            } else {
              return Err(unexpected("no this arg in non-static method"));
            }
          }
        } else {
          "".to_string()
        };
        let template_args = match method.cpp_method.template_arguments_values {
          Some(ref args) => {
            let mut texts = Vec::new();
            for arg in args {
              texts.push(try!(arg.to_cpp_code(None)));
            }
            format!("<{}>", texts.join(", "))
          }
          None => String::new(),
        };
        format!("{}{}{}",
                scope_specifier,
                method.cpp_method.name,
                template_args)
      };
      format!("{}({})",
              result_without_args,
              try!(self.arguments_values(method)))
    };
    self.convert_return_type(method, result)
  }

  /// Generates body of the FFI method implementation.
  fn source_body(&self, method: &CppAndFfiMethod) -> Result<String> {
    if method.cpp_method.is_destructor() &&
       method.allocation_place == ReturnValueAllocationPlace::Heap {
      if let Some(arg) = method.c_signature
        .arguments
        .iter()
        .find(|x| x.meaning == CppFfiArgumentMeaning::This) {
        Ok(format!("delete {};\n", arg.name))
      } else {
        panic!("Error: no this argument found\n{:?}", method);
      }
    } else {
      Ok(format!("{}{};\n",
                 if method.c_signature.return_type.ffi_type.is_void() {
                   ""
                 } else {
                   "return "
                 },
                 try!(self.returned_expression(&method))))
    }
  }

  /// Generates implementation of the FFI method for the source file.
  fn function_implementation(&self, method: &CppAndFfiMethod) -> Result<String> {
    Ok(format!("{} {{\n  {}}}\n\n",
               try!(self.function_signature(method)),
               try!(self.source_body(&method))))
  }

  /// Generates main files and directories of the library.
  pub fn generate_template_files(&self,
                                 cpp_lib_include_file: &str,
                                 include_directories: &[String],
                                 framework_directories: &[String])
                                 -> Result<()> {
    let name_upper = self.lib_name.to_uppercase();
    let cmakelists_path = self.lib_path.with_added("CMakeLists.txt");
    let mut cmakelists_file = try!(create_file(&cmakelists_path));
    let mut cxx_flags = String::new();
    if !is_msvc() {
      cxx_flags.push_str("-fPIC -std=gnu++11");
    }
    for dir in framework_directories {
      cxx_flags.push_str(&format!(" -F\\\"{}\\\"", dir));
    }
    try!(cmakelists_file.write(format!(include_str!("../templates/c_lib/CMakeLists.txt"),
                                       lib_name_lowercase = &self.lib_name,
                                       lib_name_uppercase = name_upper,
                                       include_directories = include_directories.into_iter()
                                         .map(|x| format!("\"{}\"", x.replace(r"\", r"\\")))
                                         .join(" "),
                                       library_type =
                                         if self.is_shared { "SHARED" } else { "STATIC" },
                                       target_link_libraries = if self.is_shared {
                                         format!("target_link_libraries({} {})",
                                                 &self.lib_name,
                                                 self.cpp_libs.join(" "))
                                       } else {
                                         String::new()
                                       },
                                       cxx_flags = cxx_flags)));
    let src_dir = self.lib_path.with_added("src");
    try!(create_dir_all(&src_dir));

    let include_dir = self.lib_path.with_added("include");
    try!(create_dir_all(&include_dir));
    let exports_file_path = include_dir.with_added(format!("{}_exports.h", &self.lib_name));
    let mut exports_file = try!(create_file(&exports_file_path));
    try!(exports_file.write(format!(include_str!("../templates/c_lib/exports.h"),
                                    lib_name_uppercase = name_upper)));

    let global_file_path = include_dir.with_added(format!("{}_global.h", &self.lib_name));
    let mut global_file = try!(create_file(&global_file_path));
    try!(global_file.write(format!(include_str!("../templates/c_lib/global.h"),
                                   lib_name_lowercase = &self.lib_name,
                                   lib_name_uppercase = name_upper,
                                   cpp_lib_include_file = cpp_lib_include_file)));
    Ok(())
  }

  pub fn generate_files(&self, data: &[CppFfiHeaderData]) -> Result<()> {
    try!(self.generate_all_headers_file(data.iter().map(|x| &x.include_file)));
    for item in data {
      try!(self.generate_one(item).chain_err(|| "C++ code generator failed"));
    }
    Ok(())
  }

  /// Generates the header file that includes all other headers of the library.
  fn generate_all_headers_file<'a, I: Iterator<Item = &'a String>>(&self, names: I) -> Result<()> {
    let mut h_path = self.lib_path.clone();
    h_path.push("include");
    h_path.push(format!("{}.h", &self.lib_name));
    let mut all_header_file = try!(create_file(&h_path));
    try!(all_header_file.write(format!("#ifndef {0}_H\n#define {0}_H\n\n", &self.lib_name_upper)));
    for name in names {
      try!(all_header_file.write(format!("#include \"{}_{}.h\"\n", &self.lib_name, name)));
    }
    try!(all_header_file.write(format!("#endif // {}_H\n", &self.lib_name_upper)));
    Ok(())
  }

  /// Generates a header file and a source file for a portion of data
  /// corresponding to a header file of original C++ library.
  fn generate_one(&self, data: &CppFfiHeaderData) -> Result<()> {
    let ffi_include_file = format!("{}_{}.h", &self.lib_name, data.include_file_base_name);

    let cpp_path = self.lib_path
      .with_added("src")
      .with_added(format!("{}_{}.cpp", &self.lib_name, data.include_file_base_name));
    log::noisy(format!("Generating source file: {:?}", cpp_path));

    let h_path = self.lib_path.with_added("include").with_added(&ffi_include_file);
    log::noisy(format!("Generating header file: {:?}", h_path));

    let mut cpp_file = try!(create_file(&cpp_path));
    let mut h_file = try!(create_file(&h_path));

    try!(cpp_file.write(format!("#include \"{}\"\n\n", ffi_include_file)));
    let include_guard_name = ffi_include_file.replace(".", "_").to_uppercase();
    try!(h_file.write(format!("#ifndef {}\n#define {}\n\n",
                              include_guard_name,
                              include_guard_name)));

    try!(h_file.write(format!("#include \"{}_global.h\"\n\n", &self.lib_name)));

    try!(h_file.write("extern \"C\" {\n\n"));

    for method in &data.methods {
      try!(h_file.write(try!(self.function_declaration(method))));
      try!(cpp_file.write(try!(self.function_implementation(method))));
    }

    try!(h_file.write("\n} // extern \"C\"\n\n"));

    try!(h_file.write(format!("#endif // {}\n", include_guard_name)));
    Ok(())
  }
}
<|endoftext|>


<commit_before>use crate::back::write::create_informational_target_machine;
use crate::llvm;
use syntax_pos::symbol::Symbol;
use rustc::session::Session;
use rustc::session::config::PrintRequest;
use rustc_target::spec::MergeFunctions;
use libc::c_int;
use std::ffi::CString;
use syntax::feature_gate::UnstableFeatures;

use std::str;
use std::slice;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;

static POISONED: AtomicBool = AtomicBool::new(false);
static INIT: Once = Once::new();

pub(crate) fn init(sess: &Session) {
    unsafe {
        // Before we touch LLVM, make sure that multithreading is enabled.
        INIT.call_once(|| {
            if llvm::LLVMStartMultithreaded() != 1 {
                // use an extra bool to make sure that all future usage of LLVM
                // cannot proceed despite the Once not running more than once.
                POISONED.store(true, Ordering::SeqCst);
            }

            configure_llvm(sess);
        });

        if POISONED.load(Ordering::SeqCst) {
            bug!("couldn't enable multi-threaded LLVM");
        }
    }
}

fn require_inited() {
    INIT.call_once(|| bug!("llvm is not initialized"));
    if POISONED.load(Ordering::SeqCst) {
        bug!("couldn't enable multi-threaded LLVM");
    }
}

unsafe fn configure_llvm(sess: &Session) {
    let n_args = sess.opts.cg.llvm_args.len();
    let mut llvm_c_strs = Vec::with_capacity(n_args + 1);
    let mut llvm_args = Vec::with_capacity(n_args + 1);

    llvm::LLVMRustInstallFatalErrorHandler();

    {
        let mut add = |arg: &str| {
            let s = CString::new(arg).unwrap();
            llvm_args.push(s.as_ptr());
            llvm_c_strs.push(s);
        };
        add("rustc"); // fake program name
        if sess.time_llvm_passes() { add("-time-passes"); }
        if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
        if sess.opts.debugging_opts.disable_instrumentation_preinliner {
            add("-disable-preinline");
        }
        if llvm::LLVMRustIsRustLLVM() {
            match sess.opts.debugging_opts.merge_functions
                  .unwrap_or(sess.target.target.options.merge_functions) {
                MergeFunctions::Disabled |
                MergeFunctions::Trampolines => {}
                MergeFunctions::Aliases => {
                    add("-mergefunc-use-aliases");
                }
            }
        }

        // HACK(eddyb) LLVM inserts `llvm.assume` calls to preserve align attributes
        // during inlining. Unfortunately these may block other optimizations.
        add("-preserve-alignment-assumptions-during-inlining=false");

        for arg in &sess.opts.cg.llvm_args {
            add(&(*arg));
        }
    }

    llvm::LLVMInitializePasses();

    ::rustc_llvm::initialize_available_targets();

    llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
                                 llvm_args.as_ptr());
}

// WARNING: the features after applying `to_llvm_feature` must be known
// to LLVM or the feature detection code will walk past the end of the feature
// array, leading to crashes.

const ARM_WHITELIST: &[(&str, Option<&str>)] = &[
    ("aclass", Some("arm_target_feature")),
    ("mclass", Some("arm_target_feature")),
    ("rclass", Some("arm_target_feature")),
    ("dsp", Some("arm_target_feature")),
    ("neon", Some("arm_target_feature")),
    ("v5te", Some("arm_target_feature")),
    ("v6", Some("arm_target_feature")),
    ("v6k", Some("arm_target_feature")),
    ("v6t2", Some("arm_target_feature")),
    ("v7", Some("arm_target_feature")),
    ("v8", Some("arm_target_feature")),
    ("vfp2", Some("arm_target_feature")),
    ("vfp3", Some("arm_target_feature")),
    ("vfp4", Some("arm_target_feature")),
];

const AARCH64_WHITELIST: &[(&str, Option<&str>)] = &[
    ("fp", Some("aarch64_target_feature")),
    ("neon", Some("aarch64_target_feature")),
    ("sve", Some("aarch64_target_feature")),
    ("crc", Some("aarch64_target_feature")),
    ("crypto", Some("aarch64_target_feature")),
    ("ras", Some("aarch64_target_feature")),
    ("lse", Some("aarch64_target_feature")),
    ("rdm", Some("aarch64_target_feature")),
    ("fp16", Some("aarch64_target_feature")),
    ("rcpc", Some("aarch64_target_feature")),
    ("dotprod", Some("aarch64_target_feature")),
    ("v8.1a", Some("aarch64_target_feature")),
    ("v8.2a", Some("aarch64_target_feature")),
    ("v8.3a", Some("aarch64_target_feature")),
];

const X86_WHITELIST: &[(&str, Option<&str>)] = &[
    ("adx", Some("adx_target_feature")),
    ("aes", None),
    ("avx", None),
    ("avx2", None),
    ("avx512bw", Some("avx512_target_feature")),
    ("avx512cd", Some("avx512_target_feature")),
    ("avx512dq", Some("avx512_target_feature")),
    ("avx512er", Some("avx512_target_feature")),
    ("avx512f", Some("avx512_target_feature")),
    ("avx512ifma", Some("avx512_target_feature")),
    ("avx512pf", Some("avx512_target_feature")),
    ("avx512vbmi", Some("avx512_target_feature")),
    ("avx512vl", Some("avx512_target_feature")),
    ("avx512vpopcntdq", Some("avx512_target_feature")),
    ("bmi1", None),
    ("bmi2", None),
    ("cmpxchg16b", Some("cmpxchg16b_target_feature")),
    ("fma", None),
    ("fxsr", None),
    ("lzcnt", None),
    ("mmx", Some("mmx_target_feature")),
    ("movbe", Some("movbe_target_feature")),
    ("pclmulqdq", None),
    ("popcnt", None),
    ("rdrand", None),
    ("rdseed", None),
    ("sha", None),
    ("sse", None),
    ("sse2", None),
    ("sse3", None),
    ("sse4.1", None),
    ("sse4.2", None),
    ("sse4a", Some("sse4a_target_feature")),
    ("ssse3", None),
    ("tbm", Some("tbm_target_feature")),
    ("xsave", None),
    ("xsavec", None),
    ("xsaveopt", None),
    ("xsaves", None),
];

const HEXAGON_WHITELIST: &[(&str, Option<&str>)] = &[
    ("hvx", Some("hexagon_target_feature")),
    ("hvx-double", Some("hexagon_target_feature")),
];

const POWERPC_WHITELIST: &[(&str, Option<&str>)] = &[
    ("altivec", Some("powerpc_target_feature")),
    ("power8-altivec", Some("powerpc_target_feature")),
    ("power9-altivec", Some("powerpc_target_feature")),
    ("power8-vector", Some("powerpc_target_feature")),
    ("power9-vector", Some("powerpc_target_feature")),
    ("vsx", Some("powerpc_target_feature")),
];

const MIPS_WHITELIST: &[(&str, Option<&str>)] = &[
    ("fp64", Some("mips_target_feature")),
    ("msa", Some("mips_target_feature")),
];

const WASM_WHITELIST: &[(&str, Option<&str>)] = &[
    ("simd128", Some("wasm_target_feature")),
    ("atomics", Some("wasm_target_feature")),
];

/// When rustdoc is running, provide a list of all known features so that all their respective
/// primitives may be documented.
///
/// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this
/// iterator!
pub fn all_known_features() -> impl Iterator<Item=(&'static str, Option<&'static str>)> {
    ARM_WHITELIST.iter().cloned()
        .chain(AARCH64_WHITELIST.iter().cloned())
        .chain(X86_WHITELIST.iter().cloned())
        .chain(HEXAGON_WHITELIST.iter().cloned())
        .chain(POWERPC_WHITELIST.iter().cloned())
        .chain(MIPS_WHITELIST.iter().cloned())
        .chain(WASM_WHITELIST.iter().cloned())
}

pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
    let arch = if sess.target.target.arch == "x86_64" {
        "x86"
    } else {
        &*sess.target.target.arch
    };
    match (arch, s) {
        ("x86", "pclmulqdq") => "pclmul",
        ("x86", "rdrand") => "rdrnd",
        ("x86", "bmi1") => "bmi",
        ("x86", "cmpxchg16b") => "cx16",
        ("aarch64", "fp") => "fp-armv8",
        ("aarch64", "fp16") => "fullfp16",
        (_, s) => s,
    }
}

pub fn target_features(sess: &Session) -> Vec<Symbol> {
    let target_machine = create_informational_target_machine(sess, true);
    target_feature_whitelist(sess)
        .iter()
        .filter_map(|&(feature, gate)| {
            if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() {
                Some(feature)
            } else {
                None
            }
        })
        .filter(|feature| {
            let llvm_feature = to_llvm_feature(sess, feature);
            let cstr = CString::new(llvm_feature).unwrap();
            unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) }
        })
        .map(|feature| Symbol::intern(feature)).collect()
}

pub fn target_feature_whitelist(sess: &Session)
    -> &'static [(&'static str, Option<&'static str>)]
{
    match &*sess.target.target.arch {
        "arm" => ARM_WHITELIST,
        "aarch64" => AARCH64_WHITELIST,
        "x86" | "x86_64" => X86_WHITELIST,
        "hexagon" => HEXAGON_WHITELIST,
        "mips" | "mips64" => MIPS_WHITELIST,
        "powerpc" | "powerpc64" => POWERPC_WHITELIST,
        // wasm32 on emscripten does not support these target features
        "wasm32" if !sess.target.target.options.is_like_emscripten => WASM_WHITELIST,
        _ => &[],
    }
}

pub fn print_version() {
    // Can be called without initializing LLVM
    unsafe {
        println!("LLVM version: {}.{}",
                 llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor());
    }
}

pub fn get_major_version() -> u32 {
    unsafe { llvm::LLVMRustVersionMajor() }
}

pub fn print_passes() {
    // Can be called without initializing LLVM
    unsafe { llvm::LLVMRustPrintPasses(); }
}

pub(crate) fn print(req: PrintRequest, sess: &Session) {
    require_inited();
    let tm = create_informational_target_machine(sess, true);
    unsafe {
        match req {
            PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
            PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm),
            _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
        }
    }
}

pub fn target_cpu(sess: &Session) -> &str {
    let name = match sess.opts.cg.target_cpu {
        Some(ref s) => &**s,
        None => &*sess.target.target.options.cpu
    };
    if name != "native" {
        return name
    }

    unsafe {
        let mut len = 0;
        let ptr = llvm::LLVMRustGetHostCPUName(&mut len);
        str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap()
    }
}
<commit_msg>Use -mergefunc-use-aliases for any LLVM >= 8<commit_after>use crate::back::write::create_informational_target_machine;
use crate::llvm;
use syntax_pos::symbol::Symbol;
use rustc::session::Session;
use rustc::session::config::PrintRequest;
use rustc_target::spec::MergeFunctions;
use libc::c_int;
use std::ffi::CString;
use syntax::feature_gate::UnstableFeatures;

use std::str;
use std::slice;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;

static POISONED: AtomicBool = AtomicBool::new(false);
static INIT: Once = Once::new();

pub(crate) fn init(sess: &Session) {
    unsafe {
        // Before we touch LLVM, make sure that multithreading is enabled.
        INIT.call_once(|| {
            if llvm::LLVMStartMultithreaded() != 1 {
                // use an extra bool to make sure that all future usage of LLVM
                // cannot proceed despite the Once not running more than once.
                POISONED.store(true, Ordering::SeqCst);
            }

            configure_llvm(sess);
        });

        if POISONED.load(Ordering::SeqCst) {
            bug!("couldn't enable multi-threaded LLVM");
        }
    }
}

fn require_inited() {
    INIT.call_once(|| bug!("llvm is not initialized"));
    if POISONED.load(Ordering::SeqCst) {
        bug!("couldn't enable multi-threaded LLVM");
    }
}

unsafe fn configure_llvm(sess: &Session) {
    let n_args = sess.opts.cg.llvm_args.len();
    let mut llvm_c_strs = Vec::with_capacity(n_args + 1);
    let mut llvm_args = Vec::with_capacity(n_args + 1);

    llvm::LLVMRustInstallFatalErrorHandler();

    {
        let mut add = |arg: &str| {
            let s = CString::new(arg).unwrap();
            llvm_args.push(s.as_ptr());
            llvm_c_strs.push(s);
        };
        add("rustc"); // fake program name
        if sess.time_llvm_passes() { add("-time-passes"); }
        if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
        if sess.opts.debugging_opts.disable_instrumentation_preinliner {
            add("-disable-preinline");
        }
        if get_major_version() >= 8 {
            match sess.opts.debugging_opts.merge_functions
                  .unwrap_or(sess.target.target.options.merge_functions) {
                MergeFunctions::Disabled |
                MergeFunctions::Trampolines => {}
                MergeFunctions::Aliases => {
                    add("-mergefunc-use-aliases");
                }
            }
        }

        // HACK(eddyb) LLVM inserts `llvm.assume` calls to preserve align attributes
        // during inlining. Unfortunately these may block other optimizations.
        add("-preserve-alignment-assumptions-during-inlining=false");

        for arg in &sess.opts.cg.llvm_args {
            add(&(*arg));
        }
    }

    llvm::LLVMInitializePasses();

    ::rustc_llvm::initialize_available_targets();

    llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
                                 llvm_args.as_ptr());
}

// WARNING: the features after applying `to_llvm_feature` must be known
// to LLVM or the feature detection code will walk past the end of the feature
// array, leading to crashes.

const ARM_WHITELIST: &[(&str, Option<&str>)] = &[
    ("aclass", Some("arm_target_feature")),
    ("mclass", Some("arm_target_feature")),
    ("rclass", Some("arm_target_feature")),
    ("dsp", Some("arm_target_feature")),
    ("neon", Some("arm_target_feature")),
    ("v5te", Some("arm_target_feature")),
    ("v6", Some("arm_target_feature")),
    ("v6k", Some("arm_target_feature")),
    ("v6t2", Some("arm_target_feature")),
    ("v7", Some("arm_target_feature")),
    ("v8", Some("arm_target_feature")),
    ("vfp2", Some("arm_target_feature")),
    ("vfp3", Some("arm_target_feature")),
    ("vfp4", Some("arm_target_feature")),
];

const AARCH64_WHITELIST: &[(&str, Option<&str>)] = &[
    ("fp", Some("aarch64_target_feature")),
    ("neon", Some("aarch64_target_feature")),
    ("sve", Some("aarch64_target_feature")),
    ("crc", Some("aarch64_target_feature")),
    ("crypto", Some("aarch64_target_feature")),
    ("ras", Some("aarch64_target_feature")),
    ("lse", Some("aarch64_target_feature")),
    ("rdm", Some("aarch64_target_feature")),
    ("fp16", Some("aarch64_target_feature")),
    ("rcpc", Some("aarch64_target_feature")),
    ("dotprod", Some("aarch64_target_feature")),
    ("v8.1a", Some("aarch64_target_feature")),
    ("v8.2a", Some("aarch64_target_feature")),
    ("v8.3a", Some("aarch64_target_feature")),
];

const X86_WHITELIST: &[(&str, Option<&str>)] = &[
    ("adx", Some("adx_target_feature")),
    ("aes", None),
    ("avx", None),
    ("avx2", None),
    ("avx512bw", Some("avx512_target_feature")),
    ("avx512cd", Some("avx512_target_feature")),
    ("avx512dq", Some("avx512_target_feature")),
    ("avx512er", Some("avx512_target_feature")),
    ("avx512f", Some("avx512_target_feature")),
    ("avx512ifma", Some("avx512_target_feature")),
    ("avx512pf", Some("avx512_target_feature")),
    ("avx512vbmi", Some("avx512_target_feature")),
    ("avx512vl", Some("avx512_target_feature")),
    ("avx512vpopcntdq", Some("avx512_target_feature")),
    ("bmi1", None),
    ("bmi2", None),
    ("cmpxchg16b", Some("cmpxchg16b_target_feature")),
    ("fma", None),
    ("fxsr", None),
    ("lzcnt", None),
    ("mmx", Some("mmx_target_feature")),
    ("movbe", Some("movbe_target_feature")),
    ("pclmulqdq", None),
    ("popcnt", None),
    ("rdrand", None),
    ("rdseed", None),
    ("sha", None),
    ("sse", None),
    ("sse2", None),
    ("sse3", None),
    ("sse4.1", None),
    ("sse4.2", None),
    ("sse4a", Some("sse4a_target_feature")),
    ("ssse3", None),
    ("tbm", Some("tbm_target_feature")),
    ("xsave", None),
    ("xsavec", None),
    ("xsaveopt", None),
    ("xsaves", None),
];

const HEXAGON_WHITELIST: &[(&str, Option<&str>)] = &[
    ("hvx", Some("hexagon_target_feature")),
    ("hvx-double", Some("hexagon_target_feature")),
];

const POWERPC_WHITELIST: &[(&str, Option<&str>)] = &[
    ("altivec", Some("powerpc_target_feature")),
    ("power8-altivec", Some("powerpc_target_feature")),
    ("power9-altivec", Some("powerpc_target_feature")),
    ("power8-vector", Some("powerpc_target_feature")),
    ("power9-vector", Some("powerpc_target_feature")),
    ("vsx", Some("powerpc_target_feature")),
];

const MIPS_WHITELIST: &[(&str, Option<&str>)] = &[
    ("fp64", Some("mips_target_feature")),
    ("msa", Some("mips_target_feature")),
];

const WASM_WHITELIST: &[(&str, Option<&str>)] = &[
    ("simd128", Some("wasm_target_feature")),
    ("atomics", Some("wasm_target_feature")),
];

/// When rustdoc is running, provide a list of all known features so that all their respective
/// primitives may be documented.
///
/// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this
/// iterator!
pub fn all_known_features() -> impl Iterator<Item=(&'static str, Option<&'static str>)> {
    ARM_WHITELIST.iter().cloned()
        .chain(AARCH64_WHITELIST.iter().cloned())
        .chain(X86_WHITELIST.iter().cloned())
        .chain(HEXAGON_WHITELIST.iter().cloned())
        .chain(POWERPC_WHITELIST.iter().cloned())
        .chain(MIPS_WHITELIST.iter().cloned())
        .chain(WASM_WHITELIST.iter().cloned())
}

pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
    let arch = if sess.target.target.arch == "x86_64" {
        "x86"
    } else {
        &*sess.target.target.arch
    };
    match (arch, s) {
        ("x86", "pclmulqdq") => "pclmul",
        ("x86", "rdrand") => "rdrnd",
        ("x86", "bmi1") => "bmi",
        ("x86", "cmpxchg16b") => "cx16",
        ("aarch64", "fp") => "fp-armv8",
        ("aarch64", "fp16") => "fullfp16",
        (_, s) => s,
    }
}

pub fn target_features(sess: &Session) -> Vec<Symbol> {
    let target_machine = create_informational_target_machine(sess, true);
    target_feature_whitelist(sess)
        .iter()
        .filter_map(|&(feature, gate)| {
            if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() {
                Some(feature)
            } else {
                None
            }
        })
        .filter(|feature| {
            let llvm_feature = to_llvm_feature(sess, feature);
            let cstr = CString::new(llvm_feature).unwrap();
            unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) }
        })
        .map(|feature| Symbol::intern(feature)).collect()
}

pub fn target_feature_whitelist(sess: &Session)
    -> &'static [(&'static str, Option<&'static str>)]
{
    match &*sess.target.target.arch {
        "arm" => ARM_WHITELIST,
        "aarch64" => AARCH64_WHITELIST,
        "x86" | "x86_64" => X86_WHITELIST,
        "hexagon" => HEXAGON_WHITELIST,
        "mips" | "mips64" => MIPS_WHITELIST,
        "powerpc" | "powerpc64" => POWERPC_WHITELIST,
        // wasm32 on emscripten does not support these target features
        "wasm32" if !sess.target.target.options.is_like_emscripten => WASM_WHITELIST,
        _ => &[],
    }
}

pub fn print_version() {
    // Can be called without initializing LLVM
    unsafe {
        println!("LLVM version: {}.{}",
                 llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor());
    }
}

pub fn get_major_version() -> u32 {
    unsafe { llvm::LLVMRustVersionMajor() }
}

pub fn print_passes() {
    // Can be called without initializing LLVM
    unsafe { llvm::LLVMRustPrintPasses(); }
}

pub(crate) fn print(req: PrintRequest, sess: &Session) {
    require_inited();
    let tm = create_informational_target_machine(sess, true);
    unsafe {
        match req {
            PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
            PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm),
            _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
        }
    }
}

pub fn target_cpu(sess: &Session) -> &str {
    let name = match sess.opts.cg.target_cpu {
        Some(ref s) => &**s,
        None => &*sess.target.target.options.cpu
    };
    if name != "native" {
        return name
    }

    unsafe {
        let mut len = 0;
        let ptr = llvm::LLVMRustGetHostCPUName(&mut len);
        str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap()
    }
}
<|endoftext|>


<commit_before>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{
        self,
        relate::{Relate, RelateResult, TypeRelation},
        ParamEnv, Ty, TyCtxt,
    },
};

#[derive(Copy, Clone, Debug)]
enum EdgeKind {
    Unwind,
    Normal,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let param_env = tcx.param_env(source.def_id());
        TypeChecker { when: &self.when, source, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    source: MirSource<'tcx>,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.source.instance,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Normal)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along non-unwind edges
                | (true, true, EdgeKind::Normal) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!(
                            "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
                            edge_kind,
                            bb,
                            src.is_cleanup,
                            bb.is_cleanup,
                        )
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }

    /// Check if src can be assigned into dest.
    /// This is not precise, it will accept some incorrect assignments.
    fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
        if src == dest {
            // Equal types, all is good.
            return true;
        }
        // Normalize projections and things like that.
        // FIXME: We need to reveal_all, as some optimizations change types in ways
        // that requires unfolding opaque types.
        let param_env = self.param_env.with_reveal_all();
        let src = self.tcx.normalize_erasing_regions(param_env, src);
        let dest = self.tcx.normalize_erasing_regions(param_env, dest);
        // It's worth checking equality again.
        if src == dest {
            return true;
        }

        // Type-changing assignments can happen when subtyping is used. While
        // all normal lifetimes are erased, higher-ranked types with their
        // late-bound lifetimes are still around and can lead to type
        // differences. So we compare ignoring lifetimes.
        struct LifetimeIgnoreRelation<'tcx> {
            tcx: TyCtxt<'tcx>,
            param_env: ty::ParamEnv<'tcx>,
        }

        impl TypeRelation<'tcx> for LifetimeIgnoreRelation<'tcx> {
            fn tcx(&self) -> TyCtxt<'tcx> {
                self.tcx
            }

            fn param_env(&self) -> ty::ParamEnv<'tcx> {
                self.param_env
            }

            fn tag(&self) -> &'static str {
                "librustc_mir::transform::validate"
            }

            fn a_is_expected(&self) -> bool {
                true
            }

            fn relate_with_variance<T: Relate<'tcx>>(
                &mut self,
                _: ty::Variance,
                a: &T,
                b: &T,
            ) -> RelateResult<'tcx, T> {
                // Ignore variance, require types to be exactly the same.
                self.relate(a, b)
            }

            fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
                if a == b {
                    // Short-circuit.
                    return Ok(a);
                }
                ty::relate::super_relate_tys(self, a, b)
            }

            fn regions(
                &mut self,
                a: ty::Region<'tcx>,
                _b: ty::Region<'tcx>,
            ) -> RelateResult<'tcx, ty::Region<'tcx>> {
                // Ignore regions.
                Ok(a)
            }

            fn consts(
                &mut self,
                a: &'tcx ty::Const<'tcx>,
                b: &'tcx ty::Const<'tcx>,
            ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
                ty::relate::super_relate_consts(self, a, b)
            }

            fn binders<T>(
                &mut self,
                a: &ty::Binder<T>,
                b: &ty::Binder<T>,
            ) -> RelateResult<'tcx, ty::Binder<T>>
            where
                T: Relate<'tcx>,
            {
                self.relate(a.skip_binder(), b.skip_binder())?;
                Ok(a.clone())
            }
        }

        // Instantiate and run relation.
        let mut relator: LifetimeIgnoreRelation<'tcx> =
            LifetimeIgnoreRelation { tcx: self.tcx, param_env };
        relator.relate(&src, &dest).is_ok()
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        match &statement.kind {
            StatementKind::Assign(box (dest, rvalue)) => {
                // LHS and RHS of the assignment must have the same type.
                let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
                let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
                if !self.mir_assign_valid_types(right_ty, left_ty) {
                    self.fail(
                        location,
                        format!(
                            "encountered `Assign` statement with incompatible types:\n\
                            left-hand side has type: {}\n\
                            right-hand side has type: {}",
                            left_ty, right_ty,
                        ),
                    );
                }
                // The sides of an assignment must not alias. Currently this just checks whether the places
                // are identical.
                match rvalue {
                    Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                        if dest == src {
                            self.fail(
                                location,
                                "encountered `Assign` statement with overlapping memory",
                            );
                        }
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_edge(location, *target, EdgeKind::Normal);
            }
            TerminatorKind::SwitchInt { targets, values, .. } => {
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_edge(location, *resume, EdgeKind::Normal);
                if let Some(drop) = drop {
                    self.check_edge(location, *drop, EdgeKind::Normal);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_edge(location, *destination, EdgeKind::Normal);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<commit_msg>fix typo<commit_after>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{
        self,
        relate::{Relate, RelateResult, TypeRelation},
        ParamEnv, Ty, TyCtxt,
    },
};

#[derive(Copy, Clone, Debug)]
enum EdgeKind {
    Unwind,
    Normal,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let param_env = tcx.param_env(source.def_id());
        TypeChecker { when: &self.when, source, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    source: MirSource<'tcx>,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.source.instance,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Normal)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along non-unwind edges
                | (true, true, EdgeKind::Normal) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!(
                            "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
                            edge_kind,
                            bb,
                            src.is_cleanup,
                            bb.is_cleanup,
                        )
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }

    /// Check if src can be assigned into dest.
    /// This is not precise, it will accept some incorrect assignments.
    fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
        if src == dest {
            // Equal types, all is good.
            return true;
        }
        // Normalize projections and things like that.
        // FIXME: We need to reveal_all, as some optimizations change types in ways
        // that require unfolding opaque types.
        let param_env = self.param_env.with_reveal_all();
        let src = self.tcx.normalize_erasing_regions(param_env, src);
        let dest = self.tcx.normalize_erasing_regions(param_env, dest);
        // It's worth checking equality again.
        if src == dest {
            return true;
        }

        // Type-changing assignments can happen when subtyping is used. While
        // all normal lifetimes are erased, higher-ranked types with their
        // late-bound lifetimes are still around and can lead to type
        // differences. So we compare ignoring lifetimes.
        struct LifetimeIgnoreRelation<'tcx> {
            tcx: TyCtxt<'tcx>,
            param_env: ty::ParamEnv<'tcx>,
        }

        impl TypeRelation<'tcx> for LifetimeIgnoreRelation<'tcx> {
            fn tcx(&self) -> TyCtxt<'tcx> {
                self.tcx
            }

            fn param_env(&self) -> ty::ParamEnv<'tcx> {
                self.param_env
            }

            fn tag(&self) -> &'static str {
                "librustc_mir::transform::validate"
            }

            fn a_is_expected(&self) -> bool {
                true
            }

            fn relate_with_variance<T: Relate<'tcx>>(
                &mut self,
                _: ty::Variance,
                a: &T,
                b: &T,
            ) -> RelateResult<'tcx, T> {
                // Ignore variance, require types to be exactly the same.
                self.relate(a, b)
            }

            fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
                if a == b {
                    // Short-circuit.
                    return Ok(a);
                }
                ty::relate::super_relate_tys(self, a, b)
            }

            fn regions(
                &mut self,
                a: ty::Region<'tcx>,
                _b: ty::Region<'tcx>,
            ) -> RelateResult<'tcx, ty::Region<'tcx>> {
                // Ignore regions.
                Ok(a)
            }

            fn consts(
                &mut self,
                a: &'tcx ty::Const<'tcx>,
                b: &'tcx ty::Const<'tcx>,
            ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
                ty::relate::super_relate_consts(self, a, b)
            }

            fn binders<T>(
                &mut self,
                a: &ty::Binder<T>,
                b: &ty::Binder<T>,
            ) -> RelateResult<'tcx, ty::Binder<T>>
            where
                T: Relate<'tcx>,
            {
                self.relate(a.skip_binder(), b.skip_binder())?;
                Ok(a.clone())
            }
        }

        // Instantiate and run relation.
        let mut relator: LifetimeIgnoreRelation<'tcx> =
            LifetimeIgnoreRelation { tcx: self.tcx, param_env };
        relator.relate(&src, &dest).is_ok()
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        match &statement.kind {
            StatementKind::Assign(box (dest, rvalue)) => {
                // LHS and RHS of the assignment must have the same type.
                let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
                let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
                if !self.mir_assign_valid_types(right_ty, left_ty) {
                    self.fail(
                        location,
                        format!(
                            "encountered `Assign` statement with incompatible types:\n\
                            left-hand side has type: {}\n\
                            right-hand side has type: {}",
                            left_ty, right_ty,
                        ),
                    );
                }
                // The sides of an assignment must not alias. Currently this just checks whether the places
                // are identical.
                match rvalue {
                    Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                        if dest == src {
                            self.fail(
                                location,
                                "encountered `Assign` statement with overlapping memory",
                            );
                        }
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_edge(location, *target, EdgeKind::Normal);
            }
            TerminatorKind::SwitchInt { targets, values, .. } => {
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_edge(location, *resume, EdgeKind::Normal);
                if let Some(drop) = drop {
                    self.check_edge(location, *drop, EdgeKind::Normal);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_edge(location, *destination, EdgeKind::Normal);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<|endoftext|>