From cc2c33a1567f3a29a446ae2985080e55c4dc2b8b Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Fri, 29 Nov 2019 16:05:28 -0500 Subject: Move Session to librustc_session --- Cargo.lock | 4 + src/librustc/lib.rs | 7 +- src/librustc/session/code_stats.rs | 177 -- src/librustc/session/config.rs | 2995 --------------------------------- src/librustc/session/filesearch.rs | 173 -- src/librustc/session/mod.rs | 1301 --------------- src/librustc/session/search_paths.rs | 72 - src/librustc_session/Cargo.toml | 4 + src/librustc_session/code_stats.rs | 177 ++ src/librustc_session/config.rs | 2998 ++++++++++++++++++++++++++++++++++ src/librustc_session/filesearch.rs | 174 ++ src/librustc_session/lib.rs | 15 + src/librustc_session/search_paths.rs | 74 + src/librustc_session/session.rs | 1299 +++++++++++++++ 14 files changed, 4746 insertions(+), 4724 deletions(-) delete mode 100644 src/librustc/session/code_stats.rs delete mode 100644 src/librustc/session/config.rs delete mode 100644 src/librustc/session/filesearch.rs delete mode 100644 src/librustc/session/mod.rs delete mode 100644 src/librustc/session/search_paths.rs create mode 100644 src/librustc_session/code_stats.rs create mode 100644 src/librustc_session/config.rs create mode 100644 src/librustc_session/filesearch.rs create mode 100644 src/librustc_session/search_paths.rs create mode 100644 src/librustc_session/session.rs diff --git a/Cargo.lock b/Cargo.lock index 93c5c49c920..b9a3e920e4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3893,9 +3893,13 @@ name = "rustc_session" version = "0.0.0" dependencies = [ "log", + "num_cpus", "rustc_data_structures", "rustc_errors", + "rustc_feature", + "rustc_fs_util", "rustc_index", + "rustc_target", "serialize", "syntax_pos", ] diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 481ae3b9329..e708c5ab6e7 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -64,7 +64,6 @@ #![recursion_limit="512"] #[macro_use] extern crate bitflags; -extern crate getopts; #[macro_use] extern crate scoped_tls; #[cfg(windows)] extern crate libc; @@ -74,10 +73,6 @@ extern crate libc; #[macro_use] extern crate syntax; #[macro_use] extern crate smallvec; -// Use the test crate here so we depend on getopts through it. This allow tools to link to both -// librustc_driver and libtest. -extern crate test as _; - #[cfg(test)] mod tests; @@ -113,7 +108,7 @@ pub mod middle { } pub mod mir; -pub mod session; +pub use rustc_session as session; pub mod traits; pub mod ty; diff --git a/src/librustc/session/code_stats.rs b/src/librustc/session/code_stats.rs deleted file mode 100644 index 5baf0c5948f..00000000000 --- a/src/librustc/session/code_stats.rs +++ /dev/null @@ -1,177 +0,0 @@ -use rustc_target::abi::{Align, Size}; -use rustc_data_structures::fx::{FxHashSet}; -use std::cmp::{self, Ordering}; -use rustc_data_structures::sync::Lock; - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct VariantInfo { - pub name: Option, - pub kind: SizeKind, - pub size: u64, - pub align: u64, - pub fields: Vec, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum SizeKind { - Exact, - Min, -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct FieldInfo { - pub name: String, - pub offset: u64, - pub size: u64, - pub align: u64, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum DataTypeKind { - Struct, - Union, - Enum, - Closure, -} - -#[derive(PartialEq, Eq, Hash, Debug)] -pub struct TypeSizeInfo { - pub kind: DataTypeKind, - pub type_description: String, - pub align: u64, - pub overall_size: u64, - pub packed: bool, - pub opt_discr_size: Option, - pub variants: Vec, -} - -#[derive(Default)] -pub struct CodeStats { - type_sizes: Lock>, -} - -impl CodeStats { - pub fn record_type_size(&self, - kind: DataTypeKind, - type_desc: S, - align: Align, - overall_size: Size, - packed: bool, - opt_discr_size: Option, - mut variants: Vec) { - // Sort variants so the largest ones are shown first. A stable sort is - // used here so that source code order is preserved for all variants - // that have the same size. - variants.sort_by(|info1, info2| { - info2.size.cmp(&info1.size) - }); - let info = TypeSizeInfo { - kind, - type_description: type_desc.to_string(), - align: align.bytes(), - overall_size: overall_size.bytes(), - packed: packed, - opt_discr_size: opt_discr_size.map(|s| s.bytes()), - variants, - }; - self.type_sizes.borrow_mut().insert(info); - } - - pub fn print_type_sizes(&self) { - let type_sizes = self.type_sizes.borrow(); - let mut sorted: Vec<_> = type_sizes.iter().collect(); - - // Primary sort: large-to-small. - // Secondary sort: description (dictionary order) - sorted.sort_by(|info1, info2| { - // (reversing cmp order to get large-to-small ordering) - match info2.overall_size.cmp(&info1.overall_size) { - Ordering::Equal => info1.type_description.cmp(&info2.type_description), - other => other, - } - }); - - for info in &sorted { - println!("print-type-size type: `{}`: {} bytes, alignment: {} bytes", - info.type_description, info.overall_size, info.align); - let indent = " "; - - let discr_size = if let Some(discr_size) = info.opt_discr_size { - println!("print-type-size {}discriminant: {} bytes", - indent, discr_size); - discr_size - } else { - 0 - }; - - // We start this at discr_size (rather than 0) because - // things like C-enums do not have variants but we still - // want the max_variant_size at the end of the loop below - // to reflect the presence of the discriminant. - let mut max_variant_size = discr_size; - - let struct_like = match info.kind { - DataTypeKind::Struct | DataTypeKind::Closure => true, - DataTypeKind::Enum | DataTypeKind::Union => false, - }; - for (i, variant_info) in info.variants.iter().enumerate() { - let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info; - let indent = if !struct_like { - let name = match name.as_ref() { - Some(name) => name.to_owned(), - None => i.to_string(), - }; - println!("print-type-size {}variant `{}`: {} bytes", - indent, name, size - discr_size); - " " - } else { - assert!(i < 1); - " " - }; - max_variant_size = cmp::max(max_variant_size, size); - - let mut min_offset = discr_size; - - // We want to print fields by increasing offset. - let mut fields = fields.clone(); - fields.sort_by_key(|f| f.offset); - - for field in fields.iter() { - let FieldInfo { ref name, offset, size, align } = *field; - - if offset > min_offset { - let pad = offset - min_offset; - println!("print-type-size {}padding: {} bytes", - indent, pad); - } - - if offset < min_offset { - // if this happens something is very wrong - println!("print-type-size {}field `.{}`: {} bytes, \ - offset: {} bytes, \ - alignment: {} bytes", - indent, name, size, offset, align); - } else if info.packed || offset == min_offset { - println!("print-type-size {}field `.{}`: {} bytes", - indent, name, size); - } else { - // Include field alignment in output only if it caused padding injection - println!("print-type-size {}field `.{}`: {} bytes, \ - alignment: {} bytes", - indent, name, size, align); - } - - min_offset = offset + size; - } - } - - assert!(max_variant_size <= info.overall_size, - "max_variant_size {} !<= {} overall_size", - max_variant_size, info.overall_size); - if max_variant_size < info.overall_size { - println!("print-type-size {}end padding: {} bytes", - indent, info.overall_size - max_variant_size); - } - } - } -} diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs deleted file mode 100644 index b2ab9939d3f..00000000000 --- a/src/librustc/session/config.rs +++ /dev/null @@ -1,2995 +0,0 @@ -//! Contains infrastructure for configuring the compiler, including parsing -//! command-line options. - -use rustc_session::lint; -use rustc_session::utils::NativeLibraryKind; -use crate::session::{early_error, early_warn, Session}; -use crate::session::search_paths::SearchPath; - -use rustc_data_structures::fx::FxHashSet; -use rustc_feature::UnstableFeatures; - -use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; -use rustc_target::spec::{Target, TargetTriple}; - -// Duplicated from syntax::ast for now -type CrateConfig = FxHashSet<(Symbol, Option)>; - -use syntax::source_map::{FileName, FilePathMapping}; -use syntax::edition::{Edition, EDITION_NAME_LIST, DEFAULT_EDITION}; -use syntax::symbol::{sym, Symbol}; - -use errors::emitter::HumanReadableErrorType; -use errors::{ColorConfig, FatalError, Handler}; - -use getopts; - -use std::collections::{BTreeMap, BTreeSet}; -use std::collections::btree_map::{ - Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, -}; -use std::fmt; -use std::str::{self, FromStr}; -use std::hash::Hasher; -use std::collections::hash_map::DefaultHasher; -use std::iter::FromIterator; -use std::path::{Path, PathBuf}; - -pub struct Config { - pub target: Target, - pub ptr_width: u32, -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum Sanitizer { - Address, - Leak, - Memory, - Thread, -} - -impl fmt::Display for Sanitizer { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Sanitizer::Address => "address".fmt(f), - Sanitizer::Leak => "leak".fmt(f), - Sanitizer::Memory => "memory".fmt(f), - Sanitizer::Thread => "thread".fmt(f), - } - } -} - -impl FromStr for Sanitizer { - type Err = (); - fn from_str(s: &str) -> Result { - match s { - "address" => Ok(Sanitizer::Address), - "leak" => Ok(Sanitizer::Leak), - "memory" => Ok(Sanitizer::Memory), - "thread" => Ok(Sanitizer::Thread), - _ => Err(()), - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum OptLevel { - No, // -O0 - Less, // -O1 - Default, // -O2 - Aggressive, // -O3 - Size, // -Os - SizeMin, // -Oz -} - -impl_stable_hash_via_hash!(OptLevel); - -/// This is what the `LtoCli` values get mapped to after resolving defaults and -/// and taking other command line options into account. -#[derive(Clone, PartialEq)] -pub enum Lto { - /// Don't do any LTO whatsoever - No, - - /// Do a full crate graph LTO with ThinLTO - Thin, - - /// Do a local graph LTO with ThinLTO (only relevant for multiple codegen - /// units). - ThinLocal, - - /// Do a full crate graph LTO with "fat" LTO - Fat, -} - -/// The different settings that the `-C lto` flag can have. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum LtoCli { - /// `-C lto=no` - No, - /// `-C lto=yes` - Yes, - /// `-C lto` - NoParam, - /// `-C lto=thin` - Thin, - /// `-C lto=fat` - Fat, - /// No `-C lto` flag passed - Unspecified, -} - -#[derive(Clone, PartialEq, Hash)] -pub enum LinkerPluginLto { - LinkerPlugin(PathBuf), - LinkerPluginAuto, - Disabled -} - -impl LinkerPluginLto { - pub fn enabled(&self) -> bool { - match *self { - LinkerPluginLto::LinkerPlugin(_) | - LinkerPluginLto::LinkerPluginAuto => true, - LinkerPluginLto::Disabled => false, - } - } -} - -#[derive(Clone, PartialEq, Hash)] -pub enum SwitchWithOptPath { - Enabled(Option), - Disabled, -} - -impl SwitchWithOptPath { - pub fn enabled(&self) -> bool { - match *self { - SwitchWithOptPath::Enabled(_) => true, - SwitchWithOptPath::Disabled => false, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] -pub enum SymbolManglingVersion { - Legacy, - V0, -} - -impl_stable_hash_via_hash!(SymbolManglingVersion); - -#[derive(Clone, Copy, PartialEq, Hash)] -pub enum DebugInfo { - None, - Limited, - Full, -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, RustcEncodable, RustcDecodable)] -pub enum OutputType { - Bitcode, - Assembly, - LlvmAssembly, - Mir, - Metadata, - Object, - Exe, - DepInfo, -} - -impl_stable_hash_via_hash!(OutputType); - -impl OutputType { - fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool { - match *self { - OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true, - OutputType::Bitcode - | OutputType::Assembly - | OutputType::LlvmAssembly - | OutputType::Mir - | OutputType::Object => false, - } - } - - fn shorthand(&self) -> &'static str { - match *self { - OutputType::Bitcode => "llvm-bc", - OutputType::Assembly => "asm", - OutputType::LlvmAssembly => "llvm-ir", - OutputType::Mir => "mir", - OutputType::Object => "obj", - OutputType::Metadata => "metadata", - OutputType::Exe => "link", - OutputType::DepInfo => "dep-info", - } - } - - fn from_shorthand(shorthand: &str) -> Option { - Some(match shorthand { - "asm" => OutputType::Assembly, - "llvm-ir" => OutputType::LlvmAssembly, - "mir" => OutputType::Mir, - "llvm-bc" => OutputType::Bitcode, - "obj" => OutputType::Object, - "metadata" => OutputType::Metadata, - "link" => OutputType::Exe, - "dep-info" => OutputType::DepInfo, - _ => return None, - }) - } - - fn shorthands_display() -> String { - format!( - "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", - OutputType::Bitcode.shorthand(), - OutputType::Assembly.shorthand(), - OutputType::LlvmAssembly.shorthand(), - OutputType::Mir.shorthand(), - OutputType::Object.shorthand(), - OutputType::Metadata.shorthand(), - OutputType::Exe.shorthand(), - OutputType::DepInfo.shorthand(), - ) - } - - pub fn extension(&self) -> &'static str { - match *self { - OutputType::Bitcode => "bc", - OutputType::Assembly => "s", - OutputType::LlvmAssembly => "ll", - OutputType::Mir => "mir", - OutputType::Object => "o", - OutputType::Metadata => "rmeta", - OutputType::DepInfo => "d", - OutputType::Exe => "", - } - } -} - -/// The type of diagnostics output to generate. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ErrorOutputType { - /// Output meant for the consumption of humans. - HumanReadable(HumanReadableErrorType), - /// Output that's consumed by other tools such as `rustfix` or the `RLS`. - Json { - /// Render the JSON in a human readable way (with indents and newlines). - pretty: bool, - /// The JSON output includes a `rendered` field that includes the rendered - /// human output. - json_rendered: HumanReadableErrorType, - }, -} - -impl Default for ErrorOutputType { - fn default() -> Self { - Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto)) - } -} - -/// Use tree-based collections to cheaply get a deterministic `Hash` implementation. -/// *Do not* switch `BTreeMap` out for an unsorted container type! That would break -/// dependency tracking for command-line arguments. -#[derive(Clone, Hash)] -pub struct OutputTypes(BTreeMap>); - -impl_stable_hash_via_hash!(OutputTypes); - -impl OutputTypes { - pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { - OutputTypes(BTreeMap::from_iter( - entries.iter().map(|&(k, ref v)| (k, v.clone())), - )) - } - - pub fn get(&self, key: &OutputType) -> Option<&Option> { - self.0.get(key) - } - - pub fn contains_key(&self, key: &OutputType) -> bool { - self.0.contains_key(key) - } - - pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option> { - self.0.keys() - } - - pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option> { - self.0.values() - } - - pub fn len(&self) -> usize { - self.0.len() - } - - // Returns `true` if any of the output types require codegen or linking. - pub fn should_codegen(&self) -> bool { - self.0.keys().any(|k| match *k { - OutputType::Bitcode - | OutputType::Assembly - | OutputType::LlvmAssembly - | OutputType::Mir - | OutputType::Object - | OutputType::Exe => true, - OutputType::Metadata | OutputType::DepInfo => false, - }) - } -} - -/// Use tree-based collections to cheaply get a deterministic `Hash` implementation. -/// *Do not* switch `BTreeMap` or `BTreeSet` out for an unsorted container type! That -/// would break dependency tracking for command-line arguments. -#[derive(Clone)] -pub struct Externs(BTreeMap); - -#[derive(Clone, Debug, Default)] -pub struct ExternEntry { - pub locations: BTreeSet>, - pub is_private_dep: bool -} - -impl Externs { - pub fn new(data: BTreeMap) -> Externs { - Externs(data) - } - - pub fn get(&self, key: &str) -> Option<&ExternEntry> { - self.0.get(key) - } - - pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> { - self.0.iter() - } -} - - -macro_rules! hash_option { - ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [UNTRACKED]) => ({}); - ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [TRACKED]) => ({ - if $sub_hashes.insert(stringify!($opt_name), - $opt_expr as &dyn dep_tracking::DepTrackingHash).is_some() { - bug!("duplicate key in CLI DepTrackingHash: {}", stringify!($opt_name)) - } - }); -} - -macro_rules! top_level_options { - (pub struct Options { $( - $opt:ident : $t:ty [$dep_tracking_marker:ident $($warn_val:expr, $warn_text:expr)*], - )* } ) => ( - #[derive(Clone)] - pub struct Options { - $(pub $opt: $t),* - } - - impl Options { - pub fn dep_tracking_hash(&self) -> u64 { - let mut sub_hashes = BTreeMap::new(); - $({ - hash_option!($opt, - &self.$opt, - &mut sub_hashes, - [$dep_tracking_marker $($warn_val, - $warn_text, - self.error_format)*]); - })* - let mut hasher = DefaultHasher::new(); - dep_tracking::stable_hash(sub_hashes, - &mut hasher, - self.error_format); - hasher.finish() - } - } - ); -} - -// The top-level command-line options struct. -// -// For each option, one has to specify how it behaves with regard to the -// dependency tracking system of incremental compilation. This is done via the -// square-bracketed directive after the field type. The options are: -// -// [TRACKED] -// A change in the given field will cause the compiler to completely clear the -// incremental compilation cache before proceeding. -// -// [UNTRACKED] -// Incremental compilation is not influenced by this option. -// -// If you add a new option to this struct or one of the sub-structs like -// `CodegenOptions`, think about how it influences incremental compilation. If in -// doubt, specify [TRACKED], which is always "correct" but might lead to -// unnecessary re-compilation. -top_level_options!( - pub struct Options { - // The crate config requested for the session, which may be combined - // with additional crate configurations during the compile process. - crate_types: Vec [TRACKED], - optimize: OptLevel [TRACKED], - // Include the `debug_assertions` flag in dependency tracking, since it - // can influence whether overflow checks are done or not. - debug_assertions: bool [TRACKED], - debuginfo: DebugInfo [TRACKED], - lint_opts: Vec<(String, lint::Level)> [TRACKED], - lint_cap: Option [TRACKED], - describe_lints: bool [UNTRACKED], - output_types: OutputTypes [TRACKED], - search_paths: Vec [UNTRACKED], - libs: Vec<(String, Option, Option)> [TRACKED], - maybe_sysroot: Option [UNTRACKED], - - target_triple: TargetTriple [TRACKED], - - test: bool [TRACKED], - error_format: ErrorOutputType [UNTRACKED], - - // If `Some`, enable incremental compilation, using the given - // directory to store intermediate results. - incremental: Option [UNTRACKED], - - debugging_opts: DebuggingOptions [TRACKED], - prints: Vec [UNTRACKED], - // Determines which borrow checker(s) to run. This is the parsed, sanitized - // version of `debugging_opts.borrowck`, which is just a plain string. - borrowck_mode: BorrowckMode [UNTRACKED], - cg: CodegenOptions [TRACKED], - externs: Externs [UNTRACKED], - crate_name: Option [TRACKED], - // An optional name to use as the crate for std during std injection, - // written `extern crate name as std`. Defaults to `std`. Used by - // out-of-tree drivers. - alt_std_name: Option [TRACKED], - // Indicates how the compiler should treat unstable features. - unstable_features: UnstableFeatures [TRACKED], - - // Indicates whether this run of the compiler is actually rustdoc. This - // is currently just a hack and will be removed eventually, so please - // try to not rely on this too much. - actually_rustdoc: bool [TRACKED], - - // Specifications of codegen units / ThinLTO which are forced as a - // result of parsing command line options. These are not necessarily - // what rustc was invoked with, but massaged a bit to agree with - // commands like `--emit llvm-ir` which they're often incompatible with - // if we otherwise use the defaults of rustc. - cli_forced_codegen_units: Option [UNTRACKED], - cli_forced_thinlto_off: bool [UNTRACKED], - - // Remap source path prefixes in all output (messages, object files, debug, etc.). - remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED], - - edition: Edition [TRACKED], - - // `true` if we're emitting JSON blobs about each artifact produced - // by the compiler. - json_artifact_notifications: bool [TRACKED], - - pretty: Option [UNTRACKED], - } -); - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum PrintRequest { - FileNames, - Sysroot, - CrateName, - Cfg, - TargetList, - TargetCPUs, - TargetFeatures, - RelocationModels, - CodeModels, - TlsModels, - TargetSpec, - NativeStaticLibs, -} - -#[derive(Copy, Clone)] -pub enum BorrowckMode { - Mir, - Migrate, -} - -impl BorrowckMode { - /// Returns whether we should run the MIR-based borrow check, but also fall back - /// on the AST borrow check if the MIR-based one errors. - pub fn migrate(self) -> bool { - match self { - BorrowckMode::Mir => false, - BorrowckMode::Migrate => true, - } - } -} - -pub enum Input { - /// Load source code from a file. - File(PathBuf), - /// Load source code from a string. - Str { - /// A string that is shown in place of a filename. - name: FileName, - /// An anonymous string containing the source code. - input: String, - }, -} - -impl Input { - pub fn filestem(&self) -> &str { - match *self { - Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(), - Input::Str { .. } => "rust_out", - } - } - - pub fn get_input(&mut self) -> Option<&mut String> { - match *self { - Input::File(_) => None, - Input::Str { ref mut input, .. } => Some(input), - } - } - - pub fn source_name(&self) -> FileName { - match *self { - Input::File(ref ifile) => ifile.clone().into(), - Input::Str { ref name, .. } => name.clone(), - } - } -} - -#[derive(Clone, Hash)] -pub struct OutputFilenames { - pub out_directory: PathBuf, - pub out_filestem: String, - pub single_output_file: Option, - pub extra: String, - pub outputs: OutputTypes, -} - -impl_stable_hash_via_hash!(OutputFilenames); - -pub const RUST_CGU_EXT: &str = "rcgu"; - -impl OutputFilenames { - pub fn path(&self, flavor: OutputType) -> PathBuf { - self.outputs - .get(&flavor) - .and_then(|p| p.to_owned()) - .or_else(|| self.single_output_file.clone()) - .unwrap_or_else(|| self.temp_path(flavor, None)) - } - - /// Gets the path where a compilation artifact of the given type for the - /// given codegen unit should be placed on disk. If codegen_unit_name is - /// None, a path distinct from those of any codegen unit will be generated. - pub fn temp_path(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf { - let extension = flavor.extension(); - self.temp_path_ext(extension, codegen_unit_name) - } - - /// Like temp_path, but also supports things where there is no corresponding - /// OutputType, like noopt-bitcode or lto-bitcode. - pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf { - let base = self.out_directory.join(&self.filestem()); - - let mut extension = String::new(); - - if let Some(codegen_unit_name) = codegen_unit_name { - extension.push_str(codegen_unit_name); - } - - if !ext.is_empty() { - if !extension.is_empty() { - extension.push_str("."); - extension.push_str(RUST_CGU_EXT); - extension.push_str("."); - } - - extension.push_str(ext); - } - - let path = base.with_extension(&extension[..]); - path - } - - pub fn with_extension(&self, extension: &str) -> PathBuf { - self.out_directory - .join(&self.filestem()) - .with_extension(extension) - } - - pub fn filestem(&self) -> String { - format!("{}{}", self.out_filestem, self.extra) - } -} - -pub fn host_triple() -> &'static str { - // Get the host triple out of the build environment. This ensures that our - // idea of the host triple is the same as for the set of libraries we've - // actually built. We can't just take LLVM's host triple because they - // normalize all ix86 architectures to i386. - // - // Instead of grabbing the host triple (for the current host), we grab (at - // compile time) the target triple that this rustc is built with and - // calling that (at runtime) the host triple. - (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE") -} - -impl Default for Options { - fn default() -> Options { - Options { - crate_types: Vec::new(), - optimize: OptLevel::No, - debuginfo: DebugInfo::None, - lint_opts: Vec::new(), - lint_cap: None, - describe_lints: false, - output_types: OutputTypes(BTreeMap::new()), - search_paths: vec![], - maybe_sysroot: None, - target_triple: TargetTriple::from_triple(host_triple()), - test: false, - incremental: None, - debugging_opts: basic_debugging_options(), - prints: Vec::new(), - borrowck_mode: BorrowckMode::Migrate, - cg: basic_codegen_options(), - error_format: ErrorOutputType::default(), - externs: Externs(BTreeMap::new()), - crate_name: None, - alt_std_name: None, - libs: Vec::new(), - unstable_features: UnstableFeatures::Disallow, - debug_assertions: true, - actually_rustdoc: false, - cli_forced_codegen_units: None, - cli_forced_thinlto_off: false, - remap_path_prefix: Vec::new(), - edition: DEFAULT_EDITION, - json_artifact_notifications: false, - pretty: None, - } - } -} - -impl Options { - /// Returns `true` if there is a reason to build the dep graph. - pub fn build_dep_graph(&self) -> bool { - self.incremental.is_some() || self.debugging_opts.dump_dep_graph - || self.debugging_opts.query_dep_graph - } - - #[inline(always)] - pub fn enable_dep_node_debug_strs(&self) -> bool { - cfg!(debug_assertions) - && (self.debugging_opts.query_dep_graph || self.debugging_opts.incremental_info) - } - - pub fn file_path_mapping(&self) -> FilePathMapping { - FilePathMapping::new(self.remap_path_prefix.clone()) - } - - /// Returns `true` if there will be an output file generated. - pub fn will_create_output_file(&self) -> bool { - !self.debugging_opts.parse_only && // The file is just being parsed - !self.debugging_opts.ls // The file is just being queried - } - - #[inline] - pub fn share_generics(&self) -> bool { - match self.debugging_opts.share_generics { - Some(setting) => setting, - None => { - match self.optimize { - OptLevel::No | - OptLevel::Less | - OptLevel::Size | - OptLevel::SizeMin => true, - OptLevel::Default | - OptLevel::Aggressive => false, - } - } - } - } -} - -// The type of entry function, so users can have their own entry functions -#[derive(Copy, Clone, PartialEq, Hash, Debug)] -pub enum EntryFnType { - Main, - Start, -} - -impl_stable_hash_via_hash!(EntryFnType); - -#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, HashStable)] -pub enum CrateType { - Executable, - Dylib, - Rlib, - Staticlib, - Cdylib, - ProcMacro, -} - -#[derive(Clone, Hash)] -pub enum Passes { - Some(Vec), - All, -} - -impl Passes { - pub fn is_empty(&self) -> bool { - match *self { - Passes::Some(ref v) => v.is_empty(), - Passes::All => false, - } - } -} - -/// Defines all `CodegenOptions`/`DebuggingOptions` fields and parsers all at once. The goal of this -/// macro is to define an interface that can be programmatically used by the option parser -/// to initialize the struct without hardcoding field names all over the place. -/// -/// The goal is to invoke this macro once with the correct fields, and then this macro generates all -/// necessary code. The main gotcha of this macro is the `cgsetters` module which is a bunch of -/// generated code to parse an option into its respective field in the struct. There are a few -/// hand-written parsers for parsing specific types of values in this module. -macro_rules! options { - ($struct_name:ident, $setter_name:ident, $defaultfn:ident, - $buildfn:ident, $prefix:expr, $outputname:expr, - $stat:ident, $mod_desc:ident, $mod_set:ident, - $($opt:ident : $t:ty = ( - $init:expr, - $parse:ident, - [$dep_tracking_marker:ident $(($dep_warn_val:expr, $dep_warn_text:expr))*], - $desc:expr) - ),* ,) => -( - #[derive(Clone)] - pub struct $struct_name { $(pub $opt: $t),* } - - pub fn $defaultfn() -> $struct_name { - $struct_name { $($opt: $init),* } - } - - pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $struct_name - { - let mut op = $defaultfn(); - for option in matches.opt_strs($prefix) { - let mut iter = option.splitn(2, '='); - let key = iter.next().unwrap(); - let value = iter.next(); - let option_to_lookup = key.replace("-", "_"); - let mut found = false; - for &(candidate, setter, opt_type_desc, _) in $stat { - if option_to_lookup != candidate { continue } - if !setter(&mut op, value) { - match (value, opt_type_desc) { - (Some(..), None) => { - early_error(error_format, &format!("{} option `{}` takes no \ - value", $outputname, key)) - } - (None, Some(type_desc)) => { - early_error(error_format, &format!("{0} option `{1}` requires \ - {2} ({3} {1}=)", - $outputname, key, - type_desc, $prefix)) - } - (Some(value), Some(type_desc)) => { - early_error(error_format, &format!("incorrect value `{}` for {} \ - option `{}` - {} was expected", - value, $outputname, - key, type_desc)) - } - (None, None) => bug!() - } - } - found = true; - break; - } - if !found { - early_error(error_format, &format!("unknown {} option: `{}`", - $outputname, key)); - } - } - return op; - } - - impl dep_tracking::DepTrackingHash for $struct_name { - fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { - let mut sub_hashes = BTreeMap::new(); - $({ - hash_option!($opt, - &self.$opt, - &mut sub_hashes, - [$dep_tracking_marker $($dep_warn_val, - $dep_warn_text, - error_format)*]); - })* - dep_tracking::stable_hash(sub_hashes, hasher, error_format); - } - } - - pub type $setter_name = fn(&mut $struct_name, v: Option<&str>) -> bool; - pub const $stat: &[(&str, $setter_name, Option<&str>, &str)] = - &[ $( (stringify!($opt), $mod_set::$opt, $mod_desc::$parse, $desc) ),* ]; - - #[allow(non_upper_case_globals, dead_code)] - mod $mod_desc { - pub const parse_bool: Option<&str> = None; - pub const parse_opt_bool: Option<&str> = - Some("one of: `y`, `yes`, `on`, `n`, `no`, or `off`"); - pub const parse_string: Option<&str> = Some("a string"); - pub const parse_string_push: Option<&str> = Some("a string"); - pub const parse_pathbuf_push: Option<&str> = Some("a path"); - pub const parse_opt_string: Option<&str> = Some("a string"); - pub const parse_opt_pathbuf: Option<&str> = Some("a path"); - pub const parse_list: Option<&str> = Some("a space-separated list of strings"); - pub const parse_opt_list: Option<&str> = Some("a space-separated list of strings"); - pub const parse_opt_comma_list: Option<&str> = Some("a comma-separated list of strings"); - pub const parse_threads: Option<&str> = Some("a number"); - pub const parse_uint: Option<&str> = Some("a number"); - pub const parse_passes: Option<&str> = - Some("a space-separated list of passes, or `all`"); - pub const parse_opt_uint: Option<&str> = - Some("a number"); - pub const parse_panic_strategy: Option<&str> = - Some("either `unwind` or `abort`"); - pub const parse_relro_level: Option<&str> = - Some("one of: `full`, `partial`, or `off`"); - pub const parse_sanitizer: Option<&str> = - Some("one of: `address`, `leak`, `memory` or `thread`"); - pub const parse_sanitizer_list: Option<&str> = - Some("comma separated list of sanitizers"); - pub const parse_sanitizer_memory_track_origins: Option<&str> = None; - pub const parse_linker_flavor: Option<&str> = - Some(::rustc_target::spec::LinkerFlavor::one_of()); - pub const parse_optimization_fuel: Option<&str> = - Some("crate=integer"); - pub const parse_unpretty: Option<&str> = - Some("`string` or `string=string`"); - pub const parse_treat_err_as_bug: Option<&str> = - Some("either no value or a number bigger than 0"); - pub const parse_lto: Option<&str> = - Some("either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, \ - `fat`, or omitted"); - pub const parse_linker_plugin_lto: Option<&str> = - Some("either a boolean (`yes`, `no`, `on`, `off`, etc), \ - or the path to the linker plugin"); - pub const parse_switch_with_opt_path: Option<&str> = - Some("an optional path to the profiling data output directory"); - pub const parse_merge_functions: Option<&str> = - Some("one of: `disabled`, `trampolines`, or `aliases`"); - pub const parse_symbol_mangling_version: Option<&str> = - Some("either `legacy` or `v0` (RFC 2603)"); - } - - #[allow(dead_code)] - mod $mod_set { - use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath, - SymbolManglingVersion}; - use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; - use std::path::PathBuf; - use std::str::FromStr; - - $( - pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool { - $parse(&mut cg.$opt, v) - } - )* - - fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { - match v { - Some(..) => false, - None => { *slot = true; true } - } - } - - fn parse_opt_bool(slot: &mut Option, v: Option<&str>) -> bool { - match v { - Some(s) => { - match s { - "n" | "no" | "off" => { - *slot = Some(false); - } - "y" | "yes" | "on" => { - *slot = Some(true); - } - _ => { return false; } - } - - true - }, - None => { *slot = Some(true); true } - } - } - - fn parse_opt_string(slot: &mut Option, v: Option<&str>) -> bool { - match v { - Some(s) => { *slot = Some(s.to_string()); true }, - None => false, - } - } - - fn parse_opt_pathbuf(slot: &mut Option, v: Option<&str>) -> bool { - match v { - Some(s) => { *slot = Some(PathBuf::from(s)); true }, - None => false, - } - } - - fn parse_string(slot: &mut String, v: Option<&str>) -> bool { - match v { - Some(s) => { *slot = s.to_string(); true }, - None => false, - } - } - - fn parse_string_push(slot: &mut Vec, v: Option<&str>) -> bool { - match v { - Some(s) => { slot.push(s.to_string()); true }, - None => false, - } - } - - fn parse_pathbuf_push(slot: &mut Vec, v: Option<&str>) -> bool { - match v { - Some(s) => { slot.push(PathBuf::from(s)); true }, - None => false, - } - } - - fn parse_list(slot: &mut Vec, v: Option<&str>) - -> bool { - match v { - Some(s) => { - slot.extend(s.split_whitespace().map(|s| s.to_string())); - true - }, - None => false, - } - } - - fn parse_opt_list(slot: &mut Option>, v: Option<&str>) - -> bool { - match v { - Some(s) => { - let v = s.split_whitespace().map(|s| s.to_string()).collect(); - *slot = Some(v); - true - }, - None => false, - } - } - - fn parse_opt_comma_list(slot: &mut Option>, v: Option<&str>) - -> bool { - match v { - Some(s) => { - let v = s.split(',').map(|s| s.to_string()).collect(); - *slot = Some(v); - true - }, - None => false, - } - } - - fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool { - match v.and_then(|s| s.parse().ok()) { - Some(0) => { *slot = ::num_cpus::get(); true }, - Some(i) => { *slot = i; true }, - None => false - } - } - - fn parse_uint(slot: &mut usize, v: Option<&str>) -> bool { - match v.and_then(|s| s.parse().ok()) { - Some(i) => { *slot = i; true }, - None => false - } - } - - fn parse_opt_uint(slot: &mut Option, v: Option<&str>) -> bool { - match v { - Some(s) => { *slot = s.parse().ok(); slot.is_some() } - None => { *slot = None; false } - } - } - - fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool { - match v { - Some("all") => { - *slot = Passes::All; - true - } - v => { - let mut passes = vec![]; - if parse_list(&mut passes, v) { - *slot = Passes::Some(passes); - true - } else { - false - } - } - } - } - - fn parse_panic_strategy(slot: &mut Option, v: Option<&str>) -> bool { - match v { - Some("unwind") => *slot = Some(PanicStrategy::Unwind), - Some("abort") => *slot = Some(PanicStrategy::Abort), - _ => return false - } - true - } - - fn parse_relro_level(slot: &mut Option, v: Option<&str>) -> bool { - match v { - Some(s) => { - match s.parse::() { - Ok(level) => *slot = Some(level), - _ => return false - } - }, - _ => return false - } - true - } - - fn parse_sanitizer(slot: &mut Option, v: Option<&str>) -> bool { - if let Some(Ok(s)) = v.map(str::parse) { - *slot = Some(s); - true - } else { - false - } - } - - fn parse_sanitizer_list(slot: &mut Vec, v: Option<&str>) -> bool { - if let Some(v) = v { - for s in v.split(',').map(str::parse) { - if let Ok(s) = s { - if !slot.contains(&s) { - slot.push(s); - } - } else { - return false; - } - } - true - } else { - false - } - } - - fn parse_sanitizer_memory_track_origins(slot: &mut usize, v: Option<&str>) -> bool { - match v.map(|s| s.parse()) { - None => { - *slot = 2; - true - } - Some(Ok(i)) if i <= 2 => { - *slot = i; - true - } - _ => { - false - } - } - } - - fn parse_linker_flavor(slote: &mut Option, v: Option<&str>) -> bool { - match v.and_then(LinkerFlavor::from_str) { - Some(lf) => *slote = Some(lf), - _ => return false, - } - true - } - - fn parse_optimization_fuel(slot: &mut Option<(String, u64)>, v: Option<&str>) -> bool { - match v { - None => false, - Some(s) => { - let parts = s.split('=').collect::>(); - if parts.len() != 2 { return false; } - let crate_name = parts[0].to_string(); - let fuel = parts[1].parse::(); - if fuel.is_err() { return false; } - *slot = Some((crate_name, fuel.unwrap())); - true - } - } - } - - fn parse_unpretty(slot: &mut Option, v: Option<&str>) -> bool { - match v { - None => false, - Some(s) if s.split('=').count() <= 2 => { - *slot = Some(s.to_string()); - true - } - _ => false, - } - } - - fn parse_treat_err_as_bug(slot: &mut Option, v: Option<&str>) -> bool { - match v { - Some(s) => { *slot = s.parse().ok().filter(|&x| x != 0); slot.unwrap_or(0) != 0 } - None => { *slot = Some(1); true } - } - } - - fn parse_lto(slot: &mut LtoCli, v: Option<&str>) -> bool { - if v.is_some() { - let mut bool_arg = None; - if parse_opt_bool(&mut bool_arg, v) { - *slot = if bool_arg.unwrap() { - LtoCli::Yes - } else { - LtoCli::No - }; - return true - } - } - - *slot = match v { - None => LtoCli::NoParam, - Some("thin") => LtoCli::Thin, - Some("fat") => LtoCli::Fat, - Some(_) => return false, - }; - true - } - - fn parse_linker_plugin_lto(slot: &mut LinkerPluginLto, v: Option<&str>) -> bool { - if v.is_some() { - let mut bool_arg = None; - if parse_opt_bool(&mut bool_arg, v) { - *slot = if bool_arg.unwrap() { - LinkerPluginLto::LinkerPluginAuto - } else { - LinkerPluginLto::Disabled - }; - return true - } - } - - *slot = match v { - None => LinkerPluginLto::LinkerPluginAuto, - Some(path) => LinkerPluginLto::LinkerPlugin(PathBuf::from(path)), - }; - true - } - - fn parse_switch_with_opt_path(slot: &mut SwitchWithOptPath, v: Option<&str>) -> bool { - *slot = match v { - None => SwitchWithOptPath::Enabled(None), - Some(path) => SwitchWithOptPath::Enabled(Some(PathBuf::from(path))), - }; - true - } - - fn parse_merge_functions(slot: &mut Option, v: Option<&str>) -> bool { - match v.and_then(|s| MergeFunctions::from_str(s).ok()) { - Some(mergefunc) => *slot = Some(mergefunc), - _ => return false, - } - true - } - - fn parse_symbol_mangling_version( - slot: &mut SymbolManglingVersion, - v: Option<&str>, - ) -> bool { - *slot = match v { - Some("legacy") => SymbolManglingVersion::Legacy, - Some("v0") => SymbolManglingVersion::V0, - _ => return false, - }; - true - } - } -) } - -options! {CodegenOptions, CodegenSetter, basic_codegen_options, - build_codegen_options, "C", "codegen", - CG_OPTIONS, cg_type_desc, cgsetters, - ar: Option = (None, parse_opt_string, [UNTRACKED], - "this option is deprecated and does nothing"), - linker: Option = (None, parse_opt_pathbuf, [UNTRACKED], - "system linker to link outputs with"), - link_arg: Vec = (vec![], parse_string_push, [UNTRACKED], - "a single extra argument to append to the linker invocation (can be used several times)"), - link_args: Option> = (None, parse_opt_list, [UNTRACKED], - "extra arguments to append to the linker invocation (space separated)"), - link_dead_code: bool = (false, parse_bool, [UNTRACKED], - "don't let linker strip dead code (turning it on can be used for code coverage)"), - lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED], - "perform LLVM link-time optimizations"), - target_cpu: Option = (None, parse_opt_string, [TRACKED], - "select target processor (`rustc --print target-cpus` for details)"), - target_feature: String = (String::new(), parse_string, [TRACKED], - "target specific attributes. (`rustc --print target-features` for details). \ - This feature is unsafe."), - passes: Vec = (Vec::new(), parse_list, [TRACKED], - "a list of extra LLVM passes to run (space separated)"), - llvm_args: Vec = (Vec::new(), parse_list, [TRACKED], - "a list of arguments to pass to LLVM (space separated)"), - save_temps: bool = (false, parse_bool, [UNTRACKED], - "save all temporary output files during compilation"), - rpath: bool = (false, parse_bool, [UNTRACKED], - "set rpath values in libs/exes"), - overflow_checks: Option = (None, parse_opt_bool, [TRACKED], - "use overflow checks for integer arithmetic"), - no_prepopulate_passes: bool = (false, parse_bool, [TRACKED], - "don't pre-populate the pass manager with a list of passes"), - no_vectorize_loops: bool = (false, parse_bool, [TRACKED], - "don't run the loop vectorization optimization passes"), - no_vectorize_slp: bool = (false, parse_bool, [TRACKED], - "don't run LLVM's SLP vectorization pass"), - soft_float: bool = (false, parse_bool, [TRACKED], - "use soft float ABI (*eabihf targets only)"), - prefer_dynamic: bool = (false, parse_bool, [TRACKED], - "prefer dynamic linking to static linking"), - no_integrated_as: bool = (false, parse_bool, [TRACKED], - "use an external assembler rather than LLVM's integrated one"), - no_redzone: Option = (None, parse_opt_bool, [TRACKED], - "disable the use of the redzone"), - relocation_model: Option = (None, parse_opt_string, [TRACKED], - "choose the relocation model to use (`rustc --print relocation-models` for details)"), - code_model: Option = (None, parse_opt_string, [TRACKED], - "choose the code model to use (`rustc --print code-models` for details)"), - metadata: Vec = (Vec::new(), parse_list, [TRACKED], - "metadata to mangle symbol names with"), - extra_filename: String = (String::new(), parse_string, [UNTRACKED], - "extra data to put in each output filename"), - codegen_units: Option = (None, parse_opt_uint, [UNTRACKED], - "divide crate into N units to optimize in parallel"), - remark: Passes = (Passes::Some(Vec::new()), parse_passes, [UNTRACKED], - "print remarks for these optimization passes (space separated, or \"all\")"), - no_stack_check: bool = (false, parse_bool, [UNTRACKED], - "the `--no-stack-check` flag is deprecated and does nothing"), - debuginfo: Option = (None, parse_opt_uint, [TRACKED], - "debug info emission level, 0 = no debug info, 1 = line tables only, \ - 2 = full debug info with variable and type information"), - opt_level: Option = (None, parse_opt_string, [TRACKED], - "optimize with possible levels 0-3, s, or z"), - force_frame_pointers: Option = (None, parse_opt_bool, [TRACKED], - "force use of the frame pointers"), - debug_assertions: Option = (None, parse_opt_bool, [TRACKED], - "explicitly enable the `cfg(debug_assertions)` directive"), - inline_threshold: Option = (None, parse_opt_uint, [TRACKED], - "set the threshold for inlining a function (default: 225)"), - panic: Option = (None, parse_panic_strategy, - [TRACKED], "panic strategy to compile crate with"), - incremental: Option = (None, parse_opt_string, [UNTRACKED], - "enable incremental compilation"), - default_linker_libraries: Option = (None, parse_opt_bool, [UNTRACKED], - "allow the linker to link its default libraries"), - linker_flavor: Option = (None, parse_linker_flavor, [UNTRACKED], - "linker flavor"), - linker_plugin_lto: LinkerPluginLto = (LinkerPluginLto::Disabled, - parse_linker_plugin_lto, [TRACKED], - "generate build artifacts that are compatible with linker-based LTO."), - profile_generate: SwitchWithOptPath = (SwitchWithOptPath::Disabled, - parse_switch_with_opt_path, [TRACKED], - "compile the program with profiling instrumentation"), - profile_use: Option = (None, parse_opt_pathbuf, [TRACKED], - "use the given `.profdata` file for profile-guided optimization"), -} - -options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, - build_debugging_options, "Z", "debugging", - DB_OPTIONS, db_type_desc, dbsetters, - codegen_backend: Option = (None, parse_opt_string, [TRACKED], - "the backend to use"), - verbose: bool = (false, parse_bool, [UNTRACKED], - "in general, enable more debug printouts"), - span_free_formats: bool = (false, parse_bool, [UNTRACKED], - "when debug-printing compiler state, do not include spans"), // o/w tests have closure@path - identify_regions: bool = (false, parse_bool, [UNTRACKED], - "make unnamed regions display as '# (where # is some non-ident unique id)"), - borrowck: Option = (None, parse_opt_string, [UNTRACKED], - "select which borrowck is used (`mir` or `migrate`)"), - time_passes: bool = (false, parse_bool, [UNTRACKED], - "measure time of each rustc pass"), - time: bool = (false, parse_bool, [UNTRACKED], - "measure time of rustc processes"), - time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], - "measure time of each LLVM pass"), - input_stats: bool = (false, parse_bool, [UNTRACKED], - "gather statistics about the input"), - asm_comments: bool = (false, parse_bool, [TRACKED], - "generate comments into the assembly (may change behavior)"), - verify_llvm_ir: bool = (false, parse_bool, [TRACKED], - "verify LLVM IR"), - borrowck_stats: bool = (false, parse_bool, [UNTRACKED], - "gather borrowck statistics"), - no_landing_pads: bool = (false, parse_bool, [TRACKED], - "omit landing pads for unwinding"), - fewer_names: bool = (false, parse_bool, [TRACKED], - "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR)"), - meta_stats: bool = (false, parse_bool, [UNTRACKED], - "gather metadata statistics"), - print_link_args: bool = (false, parse_bool, [UNTRACKED], - "print the arguments passed to the linker"), - print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], - "prints the LLVM optimization passes being run"), - ast_json: bool = (false, parse_bool, [UNTRACKED], - "print the AST as JSON and halt"), - // We default to 1 here since we want to behave like - // a sequential compiler for now. This'll likely be adjusted - // in the future. Note that -Zthreads=0 is the way to get - // the num_cpus behavior. - threads: usize = (1, parse_threads, [UNTRACKED], - "use a thread pool with N threads"), - ast_json_noexpand: bool = (false, parse_bool, [UNTRACKED], - "print the pre-expansion AST as JSON and halt"), - ls: bool = (false, parse_bool, [UNTRACKED], - "list the symbols defined by a library crate"), - save_analysis: bool = (false, parse_bool, [UNTRACKED], - "write syntax and type analysis (in JSON format) information, in \ - addition to normal output"), - print_region_graph: bool = (false, parse_bool, [UNTRACKED], - "prints region inference graph. \ - Use with RUST_REGION_GRAPH=help for more info"), - parse_only: bool = (false, parse_bool, [UNTRACKED], - "parse only; do not compile, assemble, or link"), - dual_proc_macros: bool = (false, parse_bool, [TRACKED], - "load proc macros for both target and host, but only link to the target"), - no_codegen: bool = (false, parse_bool, [TRACKED], - "run all passes except codegen; no output"), - treat_err_as_bug: Option = (None, parse_treat_err_as_bug, [TRACKED], - "treat error number `val` that occurs as bug"), - report_delayed_bugs: bool = (false, parse_bool, [TRACKED], - "immediately print bugs registered with `delay_span_bug`"), - external_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], - "show macro backtraces even for non-local macros"), - teach: bool = (false, parse_bool, [TRACKED], - "show extended diagnostic help"), - terminal_width: Option = (None, parse_opt_uint, [UNTRACKED], - "set the current terminal width"), - panic_abort_tests: bool = (false, parse_bool, [TRACKED], - "support compiling tests with panic=abort"), - continue_parse_after_error: bool = (false, parse_bool, [TRACKED], - "attempt to recover from parse errors (experimental)"), - dep_tasks: bool = (false, parse_bool, [UNTRACKED], - "print tasks that execute and the color their dep node gets (requires debug build)"), - incremental: Option = (None, parse_opt_string, [UNTRACKED], - "enable incremental compilation (experimental)"), - incremental_queries: bool = (true, parse_bool, [UNTRACKED], - "enable incremental compilation support for queries (experimental)"), - incremental_info: bool = (false, parse_bool, [UNTRACKED], - "print high-level information about incremental reuse (or the lack thereof)"), - incremental_dump_hash: bool = (false, parse_bool, [UNTRACKED], - "dump hash information in textual format to stdout"), - incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], - "verify incr. comp. hashes of green query instances"), - incremental_ignore_spans: bool = (false, parse_bool, [UNTRACKED], - "ignore spans during ICH computation -- used for testing"), - instrument_mcount: bool = (false, parse_bool, [TRACKED], - "insert function instrument code for mcount-based tracing"), - dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], - "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"), - query_dep_graph: bool = (false, parse_bool, [UNTRACKED], - "enable queries of the dependency graph for regression testing"), - no_analysis: bool = (false, parse_bool, [UNTRACKED], - "parse and expand the source, but run no analysis"), - unstable_options: bool = (false, parse_bool, [UNTRACKED], - "adds unstable command line options to rustc interface"), - force_overflow_checks: Option = (None, parse_opt_bool, [TRACKED], - "force overflow checks on or off"), - trace_macros: bool = (false, parse_bool, [UNTRACKED], - "for every macro invocation, print its name and arguments"), - debug_macros: bool = (false, parse_bool, [TRACKED], - "emit line numbers debug info inside macros"), - generate_arange_section: bool = (true, parse_bool, [TRACKED], - "generate DWARF address ranges for faster lookups"), - keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], - "don't clear the hygiene data after analysis"), - keep_ast: bool = (false, parse_bool, [UNTRACKED], - "keep the AST after lowering it to HIR"), - show_span: Option = (None, parse_opt_string, [TRACKED], - "show spans for compiler debugging (expr|pat|ty)"), - print_type_sizes: bool = (false, parse_bool, [UNTRACKED], - "print layout information for each type encountered"), - print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], - "print the result of the monomorphization collection pass"), - mir_opt_level: usize = (1, parse_uint, [TRACKED], - "set the MIR optimization level (0-3, default: 1)"), - mutable_noalias: Option = (None, parse_opt_bool, [TRACKED], - "emit noalias metadata for mutable references (default: no)"), - dump_mir: Option = (None, parse_opt_string, [UNTRACKED], - "dump MIR state to file. - `val` is used to select which passes and functions to dump. For example: - `all` matches all passes and functions, - `foo` matches all passes for functions whose name contains 'foo', - `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo', - `foo | bar` all passes for function names containing 'foo' or 'bar'."), - - dump_mir_dir: String = (String::from("mir_dump"), parse_string, [UNTRACKED], - "the directory the MIR is dumped into"), - dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], - "in addition to `.mir` files, create graphviz `.dot` files"), - dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED], - "if set, exclude the pass number when dumping MIR (used in tests)"), - mir_emit_retag: bool = (false, parse_bool, [TRACKED], - "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0"), - perf_stats: bool = (false, parse_bool, [UNTRACKED], - "print some performance-related statistics"), - query_stats: bool = (false, parse_bool, [UNTRACKED], - "print some statistics about the query system"), - hir_stats: bool = (false, parse_bool, [UNTRACKED], - "print some statistics about AST and HIR"), - always_encode_mir: bool = (false, parse_bool, [TRACKED], - "encode MIR of all functions into the crate metadata"), - json_rendered: Option = (None, parse_opt_string, [UNTRACKED], - "describes how to render the `rendered` field of json diagnostics"), - unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], - "take the breaks off const evaluation. NOTE: this is unsound"), - osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], - "pass `-install_name @rpath/...` to the macOS linker"), - sanitizer: Option = (None, parse_sanitizer, [TRACKED], - "use a sanitizer"), - sanitizer_recover: Vec = (vec![], parse_sanitizer_list, [TRACKED], - "Enable recovery for selected sanitizers"), - sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], - "Enable origins tracking in MemorySanitizer"), - fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], - "set the optimization fuel quota for a crate"), - print_fuel: Option = (None, parse_opt_string, [TRACKED], - "make rustc print the total optimization fuel used by a crate"), - force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], - "force all crates to be `rustc_private` unstable"), - pre_link_arg: Vec = (vec![], parse_string_push, [UNTRACKED], - "a single extra argument to prepend the linker invocation (can be used several times)"), - pre_link_args: Option> = (None, parse_opt_list, [UNTRACKED], - "extra arguments to prepend to the linker invocation (space separated)"), - profile: bool = (false, parse_bool, [TRACKED], - "insert profiling code"), - disable_instrumentation_preinliner: bool = (false, parse_bool, [TRACKED], - "Disable the instrumentation pre-inliner, useful for profiling / PGO."), - relro_level: Option = (None, parse_relro_level, [TRACKED], - "choose which RELRO level to use"), - nll_facts: bool = (false, parse_bool, [UNTRACKED], - "dump facts from NLL analysis into side files"), - nll_dont_emit_read_for_match: bool = (false, parse_bool, [UNTRACKED], - "in match codegen, do not include FakeRead statements (used by mir-borrowck)"), - dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], - "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting)."), - polonius: bool = (false, parse_bool, [UNTRACKED], - "enable polonius-based borrow-checker"), - codegen_time_graph: bool = (false, parse_bool, [UNTRACKED], - "generate a graphical HTML report of time spent in codegen and LLVM"), - thinlto: Option = (None, parse_opt_bool, [TRACKED], - "enable ThinLTO when possible"), - inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], - "control whether `#[inline]` functions are in all CGUs"), - tls_model: Option = (None, parse_opt_string, [TRACKED], - "choose the TLS model to use (`rustc --print tls-models` for details)"), - saturating_float_casts: bool = (false, parse_bool, [TRACKED], - "make float->int casts UB-free: numbers outside the integer type's range are clipped to \ - the max/min integer respectively, and NaN is mapped to 0"), - human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], - "generate human-readable, predictable names for codegen units"), - dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], - "in dep-info output, omit targets for tracking dependencies of the dep-info files \ - themselves"), - unpretty: Option = (None, parse_unpretty, [UNTRACKED], - "present the input source, unstable (and less-pretty) variants; - valid types are any of the types for `--pretty`, as well as: - `expanded`, `expanded,identified`, - `expanded,hygiene` (with internal representations), - `everybody_loops` (all function bodies replaced with `loop {}`), - `hir` (the HIR), `hir,identified`, - `hir,typed` (HIR with types for each node), - `hir-tree` (dump the raw HIR), - `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR)"), - run_dsymutil: Option = (None, parse_opt_bool, [TRACKED], - "run `dsymutil` and delete intermediate object files"), - ui_testing: bool = (false, parse_bool, [UNTRACKED], - "format compiler diagnostics in a way that's better suitable for UI testing"), - embed_bitcode: bool = (false, parse_bool, [TRACKED], - "embed LLVM bitcode in object files"), - strip_debuginfo_if_disabled: Option = (None, parse_opt_bool, [TRACKED], - "tell the linker to strip debuginfo when building without debuginfo enabled."), - share_generics: Option = (None, parse_opt_bool, [TRACKED], - "make the current crate share its generic instantiations"), - chalk: bool = (false, parse_bool, [TRACKED], - "enable the experimental Chalk-based trait solving engine"), - no_parallel_llvm: bool = (false, parse_bool, [UNTRACKED], - "don't run LLVM in parallel (while keeping codegen-units and ThinLTO)"), - no_leak_check: bool = (false, parse_bool, [UNTRACKED], - "disables the 'leak check' for subtyping; unsound, but useful for tests"), - no_interleave_lints: bool = (false, parse_bool, [UNTRACKED], - "don't interleave execution of lints; allows benchmarking individual lints"), - crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], - "inject the given attribute in the crate"), - self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled, - parse_switch_with_opt_path, [UNTRACKED], - "run the self profiler and output the raw event data"), - self_profile_events: Option> = (None, parse_opt_comma_list, [UNTRACKED], - "specifies which kinds of events get recorded by the self profiler"), - emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], - "emits a section containing stack size metadata"), - plt: Option = (None, parse_opt_bool, [TRACKED], - "whether to use the PLT when calling into shared libraries; - only has effect for PIC code on systems with ELF binaries - (default: PLT is disabled if full relro is enabled)"), - merge_functions: Option = (None, parse_merge_functions, [TRACKED], - "control the operation of the MergeFunctions LLVM pass, taking - the same values as the target option of the same name"), - allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], - "only allow the listed language features to be enabled in code (space separated)"), - symbol_mangling_version: SymbolManglingVersion = (SymbolManglingVersion::Legacy, - parse_symbol_mangling_version, [TRACKED], - "which mangling version to use for symbol names"), - binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], - "include artifacts (sysroot, crate dependencies) used during compilation in dep-info"), - insert_sideeffect: bool = (false, parse_bool, [TRACKED], - "fix undefined behavior when a thread doesn't eventually make progress \ - (such as entering an empty infinite loop) by inserting llvm.sideeffect"), -} - -pub const fn default_lib_output() -> CrateType { - CrateType::Rlib -} - -pub fn default_configuration(sess: &Session) -> CrateConfig { - let end = &sess.target.target.target_endian; - let arch = &sess.target.target.arch; - let wordsz = &sess.target.target.target_pointer_width; - let os = &sess.target.target.target_os; - let env = &sess.target.target.target_env; - let vendor = &sess.target.target.target_vendor; - let min_atomic_width = sess.target.target.min_atomic_width(); - let max_atomic_width = sess.target.target.max_atomic_width(); - let atomic_cas = sess.target.target.options.atomic_cas; - - let mut ret = FxHashSet::default(); - ret.reserve(6); // the minimum number of insertions - // Target bindings. - ret.insert((Symbol::intern("target_os"), Some(Symbol::intern(os)))); - if let Some(ref fam) = sess.target.target.options.target_family { - ret.insert((Symbol::intern("target_family"), Some(Symbol::intern(fam)))); - if fam == "windows" || fam == "unix" { - ret.insert((Symbol::intern(fam), None)); - } - } - ret.insert((Symbol::intern("target_arch"), Some(Symbol::intern(arch)))); - ret.insert((Symbol::intern("target_endian"), Some(Symbol::intern(end)))); - ret.insert(( - Symbol::intern("target_pointer_width"), - Some(Symbol::intern(wordsz)), - )); - ret.insert((Symbol::intern("target_env"), Some(Symbol::intern(env)))); - ret.insert(( - Symbol::intern("target_vendor"), - Some(Symbol::intern(vendor)), - )); - if sess.target.target.options.has_elf_tls { - ret.insert((sym::target_thread_local, None)); - } - for &i in &[8, 16, 32, 64, 128] { - if i >= min_atomic_width && i <= max_atomic_width { - let mut insert_atomic = |s| { - ret.insert(( - sym::target_has_atomic_load_store, - Some(Symbol::intern(s)), - )); - if atomic_cas { - ret.insert(( - sym::target_has_atomic, - Some(Symbol::intern(s)) - )); - } - }; - let s = i.to_string(); - insert_atomic(&s); - if &s == wordsz { - insert_atomic("ptr"); - } - } - } - if let Some(s) = &sess.opts.debugging_opts.sanitizer { - let symbol = Symbol::intern(&s.to_string()); - ret.insert((sym::sanitize, Some(symbol))); - } - if sess.opts.debug_assertions { - ret.insert((Symbol::intern("debug_assertions"), None)); - } - if sess.opts.crate_types.contains(&CrateType::ProcMacro) { - ret.insert((sym::proc_macro, None)); - } - ret -} - -/// Converts the crate `cfg!` configuration from `String` to `Symbol`. -/// `rustc_interface::interface::Config` accepts this in the compiler configuration, -/// but the symbol interner is not yet set up then, so we must convert it later. -pub fn to_crate_config(cfg: FxHashSet<(String, Option)>) -> CrateConfig { - cfg.into_iter() - .map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))) - .collect() -} - -pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig { - // Combine the configuration requested by the session (command line) with - // some default and generated configuration items. - let default_cfg = default_configuration(sess); - // If the user wants a test runner, then add the test cfg. - if sess.opts.test { - user_cfg.insert((sym::test, None)); - } - user_cfg.extend(default_cfg.iter().cloned()); - user_cfg -} - -pub fn build_target_config(opts: &Options, sp: &Handler) -> Config { - let target = Target::search(&opts.target_triple).unwrap_or_else(|e| { - sp.struct_fatal(&format!("Error loading target specification: {}", e)) - .help("Use `--print target-list` for a list of built-in targets") - .emit(); - FatalError.raise(); - }); - - let ptr_width = match &target.target_pointer_width[..] { - "16" => 16, - "32" => 32, - "64" => 64, - w => sp.fatal(&format!( - "target specification was invalid: \ - unrecognized target-pointer-width {}", - w - )).raise(), - }; - - Config { - target, - ptr_width, - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum OptionStability { - Stable, - Unstable, -} - -pub struct RustcOptGroup { - pub apply: Box &mut getopts::Options>, - pub name: &'static str, - pub stability: OptionStability, -} - -impl RustcOptGroup { - pub fn is_stable(&self) -> bool { - self.stability == OptionStability::Stable - } - - pub fn stable(name: &'static str, f: F) -> RustcOptGroup - where - F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, - { - RustcOptGroup { - name, - apply: Box::new(f), - stability: OptionStability::Stable, - } - } - - pub fn unstable(name: &'static str, f: F) -> RustcOptGroup - where - F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, - { - RustcOptGroup { - name, - apply: Box::new(f), - stability: OptionStability::Unstable, - } - } -} - -// The `opt` local module holds wrappers around the `getopts` API that -// adds extra rustc-specific metadata to each option; such metadata -// is exposed by . The public -// functions below ending with `_u` are the functions that return -// *unstable* options, i.e., options that are only enabled when the -// user also passes the `-Z unstable-options` debugging flag. -mod opt { - // The `fn flag*` etc below are written so that we can use them - // in the future; do not warn about them not being used right now. - #![allow(dead_code)] - - use getopts; - use super::RustcOptGroup; - - pub type R = RustcOptGroup; - pub type S = &'static str; - - fn stable(name: S, f: F) -> R - where - F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, - { - RustcOptGroup::stable(name, f) - } - - fn unstable(name: S, f: F) -> R - where - F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, - { - RustcOptGroup::unstable(name, f) - } - - fn longer(a: S, b: S) -> S { - if a.len() > b.len() { - a - } else { - b - } - } - - pub fn opt_s(a: S, b: S, c: S, d: S) -> R { - stable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) - } - pub fn multi_s(a: S, b: S, c: S, d: S) -> R { - stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) - } - pub fn flag_s(a: S, b: S, c: S) -> R { - stable(longer(a, b), move |opts| opts.optflag(a, b, c)) - } - pub fn flagopt_s(a: S, b: S, c: S, d: S) -> R { - stable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) - } - pub fn flagmulti_s(a: S, b: S, c: S) -> R { - stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) - } - - pub fn opt(a: S, b: S, c: S, d: S) -> R { - unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) - } - pub fn multi(a: S, b: S, c: S, d: S) -> R { - unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) - } - pub fn flag(a: S, b: S, c: S) -> R { - unstable(longer(a, b), move |opts| opts.optflag(a, b, c)) - } - pub fn flagopt(a: S, b: S, c: S, d: S) -> R { - unstable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) - } - pub fn flagmulti(a: S, b: S, c: S) -> R { - unstable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) - } -} - -/// Returns the "short" subset of the rustc command line options, -/// including metadata for each option, such as whether the option is -/// part of the stable long-term interface for rustc. -pub fn rustc_short_optgroups() -> Vec { - vec![ - opt::flag_s("h", "help", "Display this message"), - opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"), - opt::multi_s( - "L", - "", - "Add a directory to the library search path. The - optional KIND can be one of dependency, crate, native, - framework, or all (the default).", - "[KIND=]PATH", - ), - opt::multi_s( - "l", - "", - "Link the generated crate(s) to the specified native - library NAME. The optional KIND can be one of - static, framework, or dylib (the default).", - "[KIND=]NAME", - ), - make_crate_type_option(), - opt::opt_s( - "", - "crate-name", - "Specify the name of the crate being built", - "NAME", - ), - opt::opt_s( - "", - "edition", - "Specify which edition of the compiler to use when compiling code.", - EDITION_NAME_LIST, - ), - opt::multi_s( - "", - "emit", - "Comma separated list of types of output for \ - the compiler to emit", - "[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]", - ), - opt::multi_s( - "", - "print", - "Compiler information to print on stdout", - "[crate-name|file-names|sysroot|cfg|target-list|\ - target-cpus|target-features|relocation-models|\ - code-models|tls-models|target-spec-json|native-static-libs]", - ), - opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), - opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), - opt::opt_s("o", "", "Write output to ", "FILENAME"), - opt::opt_s( - "", - "out-dir", - "Write output to compiler-chosen filename \ - in ", - "DIR", - ), - opt::opt_s( - "", - "explain", - "Provide a detailed explanation of an error \ - message", - "OPT", - ), - opt::flag_s("", "test", "Build a test harness"), - opt::opt_s( - "", - "target", - "Target triple for which the code is compiled", - "TARGET", - ), - opt::multi_s("W", "warn", "Set lint warnings", "OPT"), - opt::multi_s("A", "allow", "Set lint allowed", "OPT"), - opt::multi_s("D", "deny", "Set lint denied", "OPT"), - opt::multi_s("F", "forbid", "Set lint forbidden", "OPT"), - opt::multi_s( - "", - "cap-lints", - "Set the most restrictive lint level. \ - More restrictive lints are capped at this \ - level", - "LEVEL", - ), - opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), - opt::flag_s("V", "version", "Print version info and exit"), - opt::flag_s("v", "verbose", "Use verbose output"), - ] -} - -/// Returns all rustc command line options, including metadata for -/// each option, such as whether the option is part of the stable -/// long-term interface for rustc. -pub fn rustc_optgroups() -> Vec { - let mut opts = rustc_short_optgroups(); - opts.extend(vec![ - opt::multi_s( - "", - "extern", - "Specify where an external rust library is located", - "NAME[=PATH]", - ), - opt::multi_s( - "", - "extern-private", - "Specify where an extern rust library is located, marking it as a private dependency", - "NAME=PATH", - ), - opt::opt_s("", "sysroot", "Override the system root", "PATH"), - opt::multi("Z", "", "Set internal debugging options", "FLAG"), - opt::opt_s( - "", - "error-format", - "How errors and other messages are produced", - "human|json|short", - ), - opt::multi_s( - "", - "json", - "Configure the JSON output of the compiler", - "CONFIG", - ), - opt::opt_s( - "", - "color", - "Configure coloring of output: - auto = colorize, if output goes to a tty (default); - always = always colorize output; - never = never colorize output", - "auto|always|never", - ), - opt::opt( - "", - "pretty", - "Pretty-print the input instead of compiling; - valid types are: `normal` (un-annotated source), - `expanded` (crates expanded), or - `expanded,identified` (fully parenthesized, AST nodes with IDs).", - "TYPE", - ), - opt::multi_s( - "", - "remap-path-prefix", - "Remap source names in all output (compiler messages and output files)", - "FROM=TO", - ), - ]); - opts -} - -pub fn get_cmd_lint_options(matches: &getopts::Matches, - error_format: ErrorOutputType) - -> (Vec<(String, lint::Level)>, bool, Option) { - let mut lint_opts = vec![]; - let mut describe_lints = false; - - for &level in &[lint::Allow, lint::Warn, lint::Deny, lint::Forbid] { - for lint_name in matches.opt_strs(level.as_str()) { - if lint_name == "help" { - describe_lints = true; - } else { - lint_opts.push((lint_name.replace("-", "_"), level)); - } - } - } - - let lint_cap = matches.opt_str("cap-lints").map(|cap| { - lint::Level::from_str(&cap) - .unwrap_or_else(|| early_error(error_format, &format!("unknown lint level: `{}`", cap))) - }); - (lint_opts, describe_lints, lint_cap) -} - -/// Parses the `--color` flag. -pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { - match matches.opt_str("color").as_ref().map(|s| &s[..]) { - Some("auto") => ColorConfig::Auto, - Some("always") => ColorConfig::Always, - Some("never") => ColorConfig::Never, - - None => ColorConfig::Auto, - - Some(arg) => early_error( - ErrorOutputType::default(), - &format!( - "argument for `--color` must be auto, \ - always or never (instead was `{}`)", - arg - ), - ), - } -} - -/// Parse the `--json` flag. -/// -/// The first value returned is how to render JSON diagnostics, and the second -/// is whether or not artifact notifications are enabled. -pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) { - let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = - HumanReadableErrorType::Default; - let mut json_color = ColorConfig::Never; - let mut json_artifact_notifications = false; - for option in matches.opt_strs("json") { - // For now conservatively forbid `--color` with `--json` since `--json` - // won't actually be emitting any colors and anything colorized is - // embedded in a diagnostic message anyway. - if matches.opt_str("color").is_some() { - early_error( - ErrorOutputType::default(), - "cannot specify the `--color` option with `--json`", - ); - } - - for sub_option in option.split(',') { - match sub_option { - "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, - "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, - "artifacts" => json_artifact_notifications = true, - s => { - early_error( - ErrorOutputType::default(), - &format!("unknown `--json` option `{}`", s), - ) - } - } - } - } - (json_rendered(json_color), json_artifact_notifications) -} - -/// Parses the `--error-format` flag. -pub fn parse_error_format( - matches: &getopts::Matches, - color: ColorConfig, - json_rendered: HumanReadableErrorType, -) -> ErrorOutputType { - // We need the `opts_present` check because the driver will send us Matches - // with only stable options if no unstable options are used. Since error-format - // is unstable, it will not be present. We have to use `opts_present` not - // `opt_present` because the latter will panic. - let error_format = if matches.opts_present(&["error-format".to_owned()]) { - match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { - None | - Some("human") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), - Some("human-annotate-rs") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color)) - }, - Some("json") => ErrorOutputType::Json { pretty: false, json_rendered }, - Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, - Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), - - Some(arg) => early_error( - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), - &format!( - "argument for `--error-format` must be `human`, `json` or \ - `short` (instead was `{}`)", - arg - ), - ), - } - } else { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) - }; - - match error_format { - ErrorOutputType::Json { .. } => {} - - // Conservatively require that the `--json` argument is coupled with - // `--error-format=json`. This means that `--json` is specified we - // should actually be emitting JSON blobs. - _ if matches.opt_strs("json").len() > 0 => { - early_error( - ErrorOutputType::default(), - "using `--json` requires also using `--error-format=json`", - ); - } - - _ => {} - } - - return error_format; -} - -fn parse_crate_edition(matches: &getopts::Matches) -> Edition { - let edition = match matches.opt_str("edition") { - Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| - early_error( - ErrorOutputType::default(), - &format!( - "argument for `--edition` must be one of: \ - {}. (instead was `{}`)", - EDITION_NAME_LIST, - arg - ), - ), - ), - None => DEFAULT_EDITION, - }; - - if !edition.is_stable() && !nightly_options::is_nightly_build() { - early_error( - ErrorOutputType::default(), - &format!( - "edition {} is unstable and only \ - available for nightly builds of rustc.", - edition, - ) - ) - } - - edition -} - -fn check_debug_option_stability( - debugging_opts: &DebuggingOptions, - error_format: ErrorOutputType, - json_rendered: HumanReadableErrorType, -) { - if !debugging_opts.unstable_options { - if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { - early_error( - ErrorOutputType::Json { pretty: false, json_rendered }, - "`--error-format=pretty-json` is unstable", - ); - } - if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = - error_format { - early_error( - ErrorOutputType::Json { pretty: false, json_rendered }, - "`--error-format=human-annotate-rs` is unstable", - ); - } - } -} - -fn parse_output_types( - debugging_opts: &DebuggingOptions, - matches: &getopts::Matches, - error_format: ErrorOutputType, -) -> OutputTypes { - let mut output_types = BTreeMap::new(); - if !debugging_opts.parse_only { - for list in matches.opt_strs("emit") { - for output_type in list.split(',') { - let mut parts = output_type.splitn(2, '='); - let shorthand = parts.next().unwrap(); - let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| - early_error( - error_format, - &format!( - "unknown emission type: `{}` - expected one of: {}", - shorthand, - OutputType::shorthands_display(), - ), - ), - ); - let path = parts.next().map(PathBuf::from); - output_types.insert(output_type, path); - } - } - }; - if output_types.is_empty() { - output_types.insert(OutputType::Exe, None); - } - OutputTypes(output_types) -} - -fn should_override_cgus_and_disable_thinlto( - output_types: &OutputTypes, - matches: &getopts::Matches, - error_format: ErrorOutputType, - mut codegen_units: Option, -) -> (bool, Option) { - let mut disable_thinlto = false; - // Issue #30063: if user requests LLVM-related output to one - // particular path, disable codegen-units. - let incompatible: Vec<_> = output_types.0 - .iter() - .map(|ot_path| ot_path.0) - .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file()) - .map(|ot| ot.shorthand()) - .collect(); - if !incompatible.is_empty() { - match codegen_units { - Some(n) if n > 1 => { - if matches.opt_present("o") { - for ot in &incompatible { - early_warn( - error_format, - &format!( - "`--emit={}` with `-o` incompatible with \ - `-C codegen-units=N` for N > 1", - ot - ), - ); - } - early_warn(error_format, "resetting to default -C codegen-units=1"); - codegen_units = Some(1); - disable_thinlto = true; - } - } - _ => { - codegen_units = Some(1); - disable_thinlto = true; - } - } - } - - if codegen_units == Some(0) { - early_error( - error_format, - "value for codegen units must be a positive non-zero integer", - ); - } - - (disable_thinlto, codegen_units) -} - -fn check_thread_count(debugging_opts: &DebuggingOptions, error_format: ErrorOutputType) { - if debugging_opts.threads == 0 { - early_error( - error_format, - "value for threads must be a positive non-zero integer", - ); - } - - if debugging_opts.threads > 1 && debugging_opts.fuel.is_some() { - early_error( - error_format, - "optimization fuel is incompatible with multiple threads", - ); - } -} - -fn select_incremental_path( - debugging_opts: &DebuggingOptions, - cg: &CodegenOptions, - error_format: ErrorOutputType, -) -> Option { - match (&debugging_opts.incremental, &cg.incremental) { - (Some(path1), Some(path2)) => { - if path1 != path2 { - early_error( - error_format, - &format!( - "conflicting paths for `-Z incremental` and \ - `-C incremental` specified: {} versus {}", - path1, path2 - ), - ); - } else { - Some(path1) - } - } - (Some(path), None) => Some(path), - (None, Some(path)) => Some(path), - (None, None) => None, - }.map(|m| PathBuf::from(m)) -} - -fn collect_print_requests( - cg: &mut CodegenOptions, - dopts: &mut DebuggingOptions, - matches: &getopts::Matches, - error_format: ErrorOutputType, -) -> Vec { - let mut prints = Vec::::new(); - if cg.target_cpu.as_ref().map_or(false, |s| s == "help") { - prints.push(PrintRequest::TargetCPUs); - cg.target_cpu = None; - }; - if cg.target_feature == "help" { - prints.push(PrintRequest::TargetFeatures); - cg.target_feature = String::new(); - } - if cg.relocation_model.as_ref().map_or(false, |s| s == "help") { - prints.push(PrintRequest::RelocationModels); - cg.relocation_model = None; - } - if cg.code_model.as_ref().map_or(false, |s| s == "help") { - prints.push(PrintRequest::CodeModels); - cg.code_model = None; - } - if dopts - .tls_model - .as_ref() - .map_or(false, |s| s == "help") - { - prints.push(PrintRequest::TlsModels); - dopts.tls_model = None; - } - - prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s { - "crate-name" => PrintRequest::CrateName, - "file-names" => PrintRequest::FileNames, - "sysroot" => PrintRequest::Sysroot, - "cfg" => PrintRequest::Cfg, - "target-list" => PrintRequest::TargetList, - "target-cpus" => PrintRequest::TargetCPUs, - "target-features" => PrintRequest::TargetFeatures, - "relocation-models" => PrintRequest::RelocationModels, - "code-models" => PrintRequest::CodeModels, - "tls-models" => PrintRequest::TlsModels, - "native-static-libs" => PrintRequest::NativeStaticLibs, - "target-spec-json" => { - if dopts.unstable_options { - PrintRequest::TargetSpec - } else { - early_error( - error_format, - "the `-Z unstable-options` flag must also be passed to \ - enable the target-spec-json print option", - ); - } - } - req => early_error(error_format, &format!("unknown print request `{}`", req)), - })); - - prints -} - -fn parse_target_triple(matches: &getopts::Matches, error_format: ErrorOutputType) -> TargetTriple { - match matches.opt_str("target") { - Some(target) if target.ends_with(".json") => { - let path = Path::new(&target); - TargetTriple::from_path(&path).unwrap_or_else(|_| - early_error(error_format, &format!("target file {:?} does not exist", path))) - } - Some(target) => TargetTriple::TargetTriple(target), - _ => TargetTriple::from_triple(host_triple()), - } -} - -fn parse_opt_level( - matches: &getopts::Matches, - cg: &CodegenOptions, - error_format: ErrorOutputType, -) -> OptLevel { - // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able - // to use them interchangeably. However, because they're technically different flags, - // we need to work out manually which should take precedence if both are supplied (i.e. - // the rightmost flag). We do this by finding the (rightmost) position of both flags and - // comparing them. Note that if a flag is not found, its position will be `None`, which - // always compared less than `Some(_)`. - let max_o = matches.opt_positions("O").into_iter().max(); - let max_c = matches.opt_strs_pos("C").into_iter().flat_map(|(i, s)| { - if let Some("opt-level") = s.splitn(2, '=').next() { - Some(i) - } else { - None - } - }).max(); - if max_o > max_c { - OptLevel::Default - } else { - match cg.opt_level.as_ref().map(String::as_ref) { - None => OptLevel::No, - Some("0") => OptLevel::No, - Some("1") => OptLevel::Less, - Some("2") => OptLevel::Default, - Some("3") => OptLevel::Aggressive, - Some("s") => OptLevel::Size, - Some("z") => OptLevel::SizeMin, - Some(arg) => { - early_error( - error_format, - &format!( - "optimization level needs to be \ - between 0-3, s or z (instead was `{}`)", - arg - ), - ); - } - } - } -} - -fn select_debuginfo( - matches: &getopts::Matches, - cg: &CodegenOptions, - error_format: ErrorOutputType, -) -> DebugInfo { - let max_g = matches.opt_positions("g").into_iter().max(); - let max_c = matches.opt_strs_pos("C").into_iter().flat_map(|(i, s)| { - if let Some("debuginfo") = s.splitn(2, '=').next() { - Some(i) - } else { - None - } - }).max(); - if max_g > max_c { - DebugInfo::Full - } else { - match cg.debuginfo { - None | Some(0) => DebugInfo::None, - Some(1) => DebugInfo::Limited, - Some(2) => DebugInfo::Full, - Some(arg) => { - early_error( - error_format, - &format!( - "debug info level needs to be between \ - 0-2 (instead was `{}`)", - arg - ), - ); - } - } - } -} - -fn parse_libs( - matches: &getopts::Matches, - error_format: ErrorOutputType, -) -> Vec<(String, Option, Option)> { - matches - .opt_strs("l") - .into_iter() - .map(|s| { - // Parse string of the form "[KIND=]lib[:new_name]", - // where KIND is one of "dylib", "framework", "static". - let mut parts = s.splitn(2, '='); - let kind = parts.next().unwrap(); - let (name, kind) = match (parts.next(), kind) { - (None, name) => (name, None), - (Some(name), "dylib") => (name, Some(NativeLibraryKind::NativeUnknown)), - (Some(name), "framework") => (name, Some(NativeLibraryKind::NativeFramework)), - (Some(name), "static") => (name, Some(NativeLibraryKind::NativeStatic)), - (Some(name), "static-nobundle") => { - (name, Some(NativeLibraryKind::NativeStaticNobundle)) - } - (_, s) => { - early_error( - error_format, - &format!( - "unknown library kind `{}`, expected \ - one of dylib, framework, or static", - s - ), - ); - } - }; - if kind == Some(NativeLibraryKind::NativeStaticNobundle) && - !nightly_options::is_nightly_build() { - early_error( - error_format, - &format!( - "the library kind 'static-nobundle' is only \ - accepted on the nightly compiler" - ), - ); - } - let mut name_parts = name.splitn(2, ':'); - let name = name_parts.next().unwrap(); - let new_name = name_parts.next(); - (name.to_owned(), new_name.map(|n| n.to_owned()), kind) - }) - .collect() -} - -fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType) -> BorrowckMode { - match dopts.borrowck.as_ref().map(|s| &s[..]) { - None | Some("migrate") => BorrowckMode::Migrate, - Some("mir") => BorrowckMode::Mir, - Some(m) => early_error(error_format, &format!("unknown borrowck mode `{}`", m)), - } -} - -fn parse_externs( - matches: &getopts::Matches, - debugging_opts: &DebuggingOptions, - error_format: ErrorOutputType, -) -> Externs { - if matches.opt_present("extern-private") && !debugging_opts.unstable_options { - early_error( - ErrorOutputType::default(), - "'--extern-private' is unstable and only \ - available for nightly builds of rustc." - ) - } - - // We start out with a `Vec<(Option, bool)>>`, - // and later convert it into a `BTreeSet<(Option, bool)>` - // This allows to modify entries in-place to set their correct - // 'public' value. - let mut externs: BTreeMap = BTreeMap::new(); - for (arg, private) in matches.opt_strs("extern").into_iter().map(|v| (v, false)) - .chain(matches.opt_strs("extern-private").into_iter().map(|v| (v, true))) { - - let mut parts = arg.splitn(2, '='); - let name = parts.next().unwrap_or_else(|| - early_error(error_format, "--extern value must not be empty")); - let location = parts.next().map(|s| s.to_string()); - - let entry = externs - .entry(name.to_owned()) - .or_default(); - - - entry.locations.insert(location.clone()); - - // Crates start out being not private, - // and go to being private if we see an '--extern-private' - // flag - entry.is_private_dep |= private; - } - Externs(externs) -} - -fn parse_remap_path_prefix( - matches: &getopts::Matches, - error_format: ErrorOutputType -) -> Vec<(PathBuf, PathBuf)> { - matches - .opt_strs("remap-path-prefix") - .into_iter() - .map(|remap| { - let mut parts = remap.rsplitn(2, '='); // reverse iterator - let to = parts.next(); - let from = parts.next(); - match (from, to) { - (Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)), - _ => early_error( - error_format, - "--remap-path-prefix must contain '=' between FROM and TO", - ), - } - }) - .collect() -} - -pub fn build_session_options(matches: &getopts::Matches) -> Options { - let color = parse_color(matches); - - let edition = parse_crate_edition(matches); - - let (json_rendered, json_artifact_notifications) = parse_json(matches); - - let error_format = parse_error_format(matches, color, json_rendered); - - let unparsed_crate_types = matches.opt_strs("crate-type"); - let crate_types = parse_crate_types_from_list(unparsed_crate_types) - .unwrap_or_else(|e| early_error(error_format, &e[..])); - - let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); - - let mut debugging_opts = build_debugging_options(matches, error_format); - check_debug_option_stability(&debugging_opts, error_format, json_rendered); - - let output_types = parse_output_types(&debugging_opts, matches, error_format); - - let mut cg = build_codegen_options(matches, error_format); - let (disable_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( - &output_types, - matches, - error_format, - cg.codegen_units, - ); - - check_thread_count(&debugging_opts, error_format); - - let incremental = select_incremental_path(&debugging_opts, &cg, error_format); - - if debugging_opts.profile && incremental.is_some() { - early_error( - error_format, - "can't instrument with gcov profiling when compiling incrementally", - ); - } - - if cg.profile_generate.enabled() && cg.profile_use.is_some() { - early_error( - error_format, - "options `-C profile-generate` and `-C profile-use` are exclusive", - ); - } - - let prints = collect_print_requests( - &mut cg, - &mut debugging_opts, - matches, - error_format, - ); - - let cg = cg; - - let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); - let target_triple = parse_target_triple(matches, error_format); - let opt_level = parse_opt_level(matches, &cg, error_format); - // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able - // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) - // for more details. - let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No); - let debuginfo = select_debuginfo(matches, &cg, error_format); - - let mut search_paths = vec![]; - for s in &matches.opt_strs("L") { - search_paths.push(SearchPath::from_cli_opt(&s[..], error_format)); - } - - let libs = parse_libs(matches, error_format); - - let test = matches.opt_present("test"); - - let borrowck_mode = parse_borrowck_mode(&debugging_opts, error_format); - - if !cg.remark.is_empty() && debuginfo == DebugInfo::None { - early_warn( - error_format, - "-C remark requires \"-C debuginfo=n\" to show source locations", - ); - } - - let externs = parse_externs(matches, &debugging_opts, error_format); - - let crate_name = matches.opt_str("crate-name"); - - let remap_path_prefix = parse_remap_path_prefix(matches, error_format); - - let pretty = parse_pretty(matches, &debugging_opts, error_format); - - Options { - crate_types, - optimize: opt_level, - debuginfo, - lint_opts, - lint_cap, - describe_lints, - output_types, - search_paths, - maybe_sysroot: sysroot_opt, - target_triple, - test, - incremental, - debugging_opts, - prints, - borrowck_mode, - cg, - error_format, - externs, - crate_name, - alt_std_name: None, - libs, - unstable_features: UnstableFeatures::from_environment(), - debug_assertions, - actually_rustdoc: false, - cli_forced_codegen_units: codegen_units, - cli_forced_thinlto_off: disable_thinlto, - remap_path_prefix, - edition, - json_artifact_notifications, - pretty, - } -} - -fn parse_pretty( - matches: &getopts::Matches, - debugging_opts: &DebuggingOptions, - efmt: ErrorOutputType, -) -> Option { - let pretty = if debugging_opts.unstable_options { - matches.opt_default("pretty", "normal").map(|a| { - // stable pretty-print variants only - parse_pretty_inner(efmt, &a, false) - }) - } else { - None - }; - - return if pretty.is_none() { - debugging_opts.unpretty.as_ref().map(|a| { - // extended with unstable pretty-print variants - parse_pretty_inner(efmt, &a, true) - }) - } else { - pretty - }; - - fn parse_pretty_inner( - efmt: ErrorOutputType, - name: &str, - extended: bool, - ) -> PpMode { - use PpMode::*; - use PpSourceMode::*; - let first = match (name, extended) { - ("normal", _) => PpmSource(PpmNormal), - ("identified", _) => PpmSource(PpmIdentified), - ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops), - ("expanded", _) => PpmSource(PpmExpanded), - ("expanded,identified", _) => PpmSource(PpmExpandedIdentified), - ("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene), - ("hir", true) => PpmHir(PpmNormal), - ("hir,identified", true) => PpmHir(PpmIdentified), - ("hir,typed", true) => PpmHir(PpmTyped), - ("hir-tree", true) => PpmHirTree(PpmNormal), - ("mir", true) => PpmMir, - ("mir-cfg", true) => PpmMirCFG, - _ => { - if extended { - early_error(efmt, &format!("argument to `unpretty` must be one of `normal`, \ - `expanded`, `identified`, `expanded,identified`, \ - `expanded,hygiene`, `everybody_loops`, \ - `hir`, `hir,identified`, `hir,typed`, `hir-tree`, \ - `mir` or `mir-cfg`; got {}", - name)); - } else { - early_error(efmt, &format!("argument to `pretty` must be one of `normal`, \ - `expanded`, `identified`, or `expanded,identified`; got {}", - name)); - } - } - }; - first - } -} - -pub fn make_crate_type_option() -> RustcOptGroup { - opt::multi_s( - "", - "crate-type", - "Comma separated list of types of crates - for the compiler to emit", - "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]", - ) -} - -pub fn parse_crate_types_from_list(list_list: Vec) -> Result, String> { - let mut crate_types: Vec = Vec::new(); - for unparsed_crate_type in &list_list { - for part in unparsed_crate_type.split(',') { - let new_part = match part { - "lib" => default_lib_output(), - "rlib" => CrateType::Rlib, - "staticlib" => CrateType::Staticlib, - "dylib" => CrateType::Dylib, - "cdylib" => CrateType::Cdylib, - "bin" => CrateType::Executable, - "proc-macro" => CrateType::ProcMacro, - _ => return Err(format!("unknown crate type: `{}`", part)) - }; - if !crate_types.contains(&new_part) { - crate_types.push(new_part) - } - } - } - - Ok(crate_types) -} - -pub mod nightly_options { - use getopts; - use rustc_feature::UnstableFeatures; - use super::{ErrorOutputType, OptionStability, RustcOptGroup}; - use crate::session::early_error; - - pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { - is_nightly_build() - && matches - .opt_strs("Z") - .iter() - .any(|x| *x == "unstable-options") - } - - pub fn is_nightly_build() -> bool { - UnstableFeatures::from_environment().is_nightly_build() - } - - pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) { - let has_z_unstable_option = matches - .opt_strs("Z") - .iter() - .any(|x| *x == "unstable-options"); - let really_allows_unstable_options = - UnstableFeatures::from_environment().is_nightly_build(); - - for opt in flags.iter() { - if opt.stability == OptionStability::Stable { - continue; - } - if !matches.opt_present(opt.name) { - continue; - } - if opt.name != "Z" && !has_z_unstable_option { - early_error( - ErrorOutputType::default(), - &format!( - "the `-Z unstable-options` flag must also be passed to enable \ - the flag `{}`", - opt.name - ), - ); - } - if really_allows_unstable_options { - continue; - } - match opt.stability { - OptionStability::Unstable => { - let msg = format!( - "the option `{}` is only accepted on the \ - nightly compiler", - opt.name - ); - early_error(ErrorOutputType::default(), &msg); - } - OptionStability::Stable => {} - } - } - } -} - -impl fmt::Display for CrateType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - CrateType::Executable => "bin".fmt(f), - CrateType::Dylib => "dylib".fmt(f), - CrateType::Rlib => "rlib".fmt(f), - CrateType::Staticlib => "staticlib".fmt(f), - CrateType::Cdylib => "cdylib".fmt(f), - CrateType::ProcMacro => "proc-macro".fmt(f), - } - } -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum PpSourceMode { - PpmNormal, - PpmEveryBodyLoops, - PpmExpanded, - PpmIdentified, - PpmExpandedIdentified, - PpmExpandedHygiene, - PpmTyped, -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum PpMode { - PpmSource(PpSourceMode), - PpmHir(PpSourceMode), - PpmHirTree(PpSourceMode), - PpmMir, - PpmMirCFG, -} - -impl PpMode { - pub fn needs_ast_map(&self) -> bool { - use PpMode::*; - use PpSourceMode::*; - match *self { - PpmSource(PpmNormal) | - PpmSource(PpmEveryBodyLoops) | - PpmSource(PpmIdentified) => false, - - PpmSource(PpmExpanded) | - PpmSource(PpmExpandedIdentified) | - PpmSource(PpmExpandedHygiene) | - PpmHir(_) | - PpmHirTree(_) | - PpmMir | - PpmMirCFG => true, - PpmSource(PpmTyped) => panic!("invalid state"), - } - } - - pub fn needs_analysis(&self) -> bool { - use PpMode::*; - match *self { - PpmMir | PpmMirCFG => true, - _ => false, - } - } -} - -/// Command-line arguments passed to the compiler have to be incorporated with -/// the dependency tracking system for incremental compilation. This module -/// provides some utilities to make this more convenient. -/// -/// The values of all command-line arguments that are relevant for dependency -/// tracking are hashed into a single value that determines whether the -/// incremental compilation cache can be re-used or not. This hashing is done -/// via the `DepTrackingHash` trait defined below, since the standard `Hash` -/// implementation might not be suitable (e.g., arguments are stored in a `Vec`, -/// the hash of which is order dependent, but we might not want the order of -/// arguments to make a difference for the hash). -/// -/// However, since the value provided by `Hash::hash` often *is* suitable, -/// especially for primitive types, there is the -/// `impl_dep_tracking_hash_via_hash!()` macro that allows to simply reuse the -/// `Hash` implementation for `DepTrackingHash`. It's important though that -/// we have an opt-in scheme here, so one is hopefully forced to think about -/// how the hash should be calculated when adding a new command-line argument. -mod dep_tracking { - use rustc_session::lint; - use rustc_session::utils::NativeLibraryKind; - use std::collections::BTreeMap; - use std::hash::Hash; - use std::path::PathBuf; - use std::collections::hash_map::DefaultHasher; - use super::{CrateType, DebugInfo, ErrorOutputType, OptLevel, OutputTypes, - Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath, - SymbolManglingVersion}; - use rustc_feature::UnstableFeatures; - use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel, TargetTriple}; - use syntax::edition::Edition; - - pub trait DepTrackingHash { - fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType); - } - - macro_rules! impl_dep_tracking_hash_via_hash { - ($t:ty) => ( - impl DepTrackingHash for $t { - fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType) { - Hash::hash(self, hasher); - } - } - ) - } - - macro_rules! impl_dep_tracking_hash_for_sortable_vec_of { - ($t:ty) => ( - impl DepTrackingHash for Vec<$t> { - fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { - let mut elems: Vec<&$t> = self.iter().collect(); - elems.sort(); - Hash::hash(&elems.len(), hasher); - for (index, elem) in elems.iter().enumerate() { - Hash::hash(&index, hasher); - DepTrackingHash::hash(*elem, hasher, error_format); - } - } - } - ); - } - - impl_dep_tracking_hash_via_hash!(bool); - impl_dep_tracking_hash_via_hash!(usize); - impl_dep_tracking_hash_via_hash!(u64); - impl_dep_tracking_hash_via_hash!(String); - impl_dep_tracking_hash_via_hash!(PathBuf); - impl_dep_tracking_hash_via_hash!(lint::Level); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); - impl_dep_tracking_hash_via_hash!(Option>); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(CrateType); - impl_dep_tracking_hash_via_hash!(MergeFunctions); - impl_dep_tracking_hash_via_hash!(PanicStrategy); - impl_dep_tracking_hash_via_hash!(RelroLevel); - impl_dep_tracking_hash_via_hash!(Passes); - impl_dep_tracking_hash_via_hash!(OptLevel); - impl_dep_tracking_hash_via_hash!(LtoCli); - impl_dep_tracking_hash_via_hash!(DebugInfo); - impl_dep_tracking_hash_via_hash!(UnstableFeatures); - impl_dep_tracking_hash_via_hash!(OutputTypes); - impl_dep_tracking_hash_via_hash!(NativeLibraryKind); - impl_dep_tracking_hash_via_hash!(Sanitizer); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(TargetTriple); - impl_dep_tracking_hash_via_hash!(Edition); - impl_dep_tracking_hash_via_hash!(LinkerPluginLto); - impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); - impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); - - impl_dep_tracking_hash_for_sortable_vec_of!(String); - impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); - impl_dep_tracking_hash_for_sortable_vec_of!(CrateType); - impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level)); - impl_dep_tracking_hash_for_sortable_vec_of!(( - String, - Option, - Option - )); - impl_dep_tracking_hash_for_sortable_vec_of!((String, u64)); - impl_dep_tracking_hash_for_sortable_vec_of!(Sanitizer); - - impl DepTrackingHash for (T1, T2) - where - T1: DepTrackingHash, - T2: DepTrackingHash, - { - fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { - Hash::hash(&0, hasher); - DepTrackingHash::hash(&self.0, hasher, error_format); - Hash::hash(&1, hasher); - DepTrackingHash::hash(&self.1, hasher, error_format); - } - } - - impl DepTrackingHash for (T1, T2, T3) - where - T1: DepTrackingHash, - T2: DepTrackingHash, - T3: DepTrackingHash, - { - fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { - Hash::hash(&0, hasher); - DepTrackingHash::hash(&self.0, hasher, error_format); - Hash::hash(&1, hasher); - DepTrackingHash::hash(&self.1, hasher, error_format); - Hash::hash(&2, hasher); - DepTrackingHash::hash(&self.2, hasher, error_format); - } - } - - // This is a stable hash because BTreeMap is a sorted container - pub fn stable_hash( - sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, - hasher: &mut DefaultHasher, - error_format: ErrorOutputType, - ) { - for (key, sub_hash) in sub_hashes { - // Using Hash::hash() instead of DepTrackingHash::hash() is fine for - // the keys, as they are just plain strings - Hash::hash(&key.len(), hasher); - Hash::hash(key, hasher); - sub_hash.hash(hasher, error_format); - } - } -} diff --git a/src/librustc/session/filesearch.rs b/src/librustc/session/filesearch.rs deleted file mode 100644 index cf09d45ca38..00000000000 --- a/src/librustc/session/filesearch.rs +++ /dev/null @@ -1,173 +0,0 @@ -#![allow(non_camel_case_types)] - -pub use self::FileMatch::*; - -use std::borrow::Cow; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; - -use crate::session::search_paths::{SearchPath, PathKind}; -use rustc_fs_util::fix_windows_verbatim_for_gcc; - -#[derive(Copy, Clone)] -pub enum FileMatch { - FileMatches, - FileDoesntMatch, -} - -// A module for searching for libraries - -#[derive(Clone)] -pub struct FileSearch<'a> { - sysroot: &'a Path, - triple: &'a str, - search_paths: &'a [SearchPath], - tlib_path: &'a SearchPath, - kind: PathKind, -} - -impl<'a> FileSearch<'a> { - pub fn search_paths(&self) -> impl Iterator { - let kind = self.kind; - self.search_paths.iter() - .filter(move |sp| sp.kind.matches(kind)) - .chain(std::iter::once(self.tlib_path)) - } - - pub fn get_lib_path(&self) -> PathBuf { - make_target_lib_path(self.sysroot, self.triple) - } - - pub fn search(&self, mut pick: F) - where F: FnMut(&Path, PathKind) -> FileMatch - { - for search_path in self.search_paths() { - debug!("searching {}", search_path.dir.display()); - fn is_rlib(p: &Path) -> bool { - p.extension() == Some("rlib".as_ref()) - } - // Reading metadata out of rlibs is faster, and if we find both - // an rlib and a dylib we only read one of the files of - // metadata, so in the name of speed, bring all rlib files to - // the front of the search list. - let files1 = search_path.files.iter().filter(|p| is_rlib(p)); - let files2 = search_path.files.iter().filter(|p| !is_rlib(p)); - for path in files1.chain(files2) { - debug!("testing {}", path.display()); - let maybe_picked = pick(path, search_path.kind); - match maybe_picked { - FileMatches => { - debug!("picked {}", path.display()); - } - FileDoesntMatch => { - debug!("rejected {}", path.display()); - } - } - } - } - } - - pub fn new(sysroot: &'a Path, - triple: &'a str, - search_paths: &'a Vec, - tlib_path: &'a SearchPath, - kind: PathKind) - -> FileSearch<'a> { - debug!("using sysroot = {}, triple = {}", sysroot.display(), triple); - FileSearch { - sysroot, - triple, - search_paths, - tlib_path, - kind, - } - } - - // Returns just the directories within the search paths. - pub fn search_path_dirs(&self) -> Vec { - self.search_paths() - .map(|sp| sp.dir.to_path_buf()) - .collect() - } - - // Returns a list of directories where target-specific tool binaries are located. - pub fn get_tools_search_paths(&self) -> Vec { - let mut p = PathBuf::from(self.sysroot); - p.push(find_libdir(self.sysroot).as_ref()); - p.push(RUST_LIB_DIR); - p.push(&self.triple); - p.push("bin"); - vec![p] - } -} - -pub fn relative_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { - let mut p = PathBuf::from(find_libdir(sysroot).as_ref()); - assert!(p.is_relative()); - p.push(RUST_LIB_DIR); - p.push(target_triple); - p.push("lib"); - p -} - -pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { - sysroot.join(&relative_target_lib_path(sysroot, target_triple)) -} - -pub fn get_or_default_sysroot() -> PathBuf { - // Follow symlinks. If the resolved path is relative, make it absolute. - fn canonicalize(path: Option) -> Option { - path.and_then(|path| { - match fs::canonicalize(&path) { - // See comments on this target function, but the gist is that - // gcc chokes on verbatim paths which fs::canonicalize generates - // so we try to avoid those kinds of paths. - Ok(canon) => Some(fix_windows_verbatim_for_gcc(&canon)), - Err(e) => bug!("failed to get realpath: {}", e), - } - }) - } - - match env::current_exe() { - Ok(exe) => { - match canonicalize(Some(exe)) { - Some(mut p) => { p.pop(); p.pop(); p }, - None => bug!("can't determine value for sysroot") - } - } - Err(ref e) => panic!(format!("failed to get current_exe: {}", e)) - } -} - -// The name of the directory rustc expects libraries to be located. -fn find_libdir(sysroot: &Path) -> Cow<'static, str> { - // FIXME: This is a quick hack to make the rustc binary able to locate - // Rust libraries in Linux environments where libraries might be installed - // to lib64/lib32. This would be more foolproof by basing the sysroot off - // of the directory where librustc is located, rather than where the rustc - // binary is. - // If --libdir is set during configuration to the value other than - // "lib" (i.e., non-default), this value is used (see issue #16552). - - #[cfg(target_pointer_width = "64")] - const PRIMARY_LIB_DIR: &str = "lib64"; - - #[cfg(target_pointer_width = "32")] - const PRIMARY_LIB_DIR: &str = "lib32"; - - const SECONDARY_LIB_DIR: &str = "lib"; - - match option_env!("CFG_LIBDIR_RELATIVE") { - Some(libdir) if libdir != "lib" => libdir.into(), - _ => if sysroot.join(PRIMARY_LIB_DIR).join(RUST_LIB_DIR).exists() { - PRIMARY_LIB_DIR.into() - } else { - SECONDARY_LIB_DIR.into() - } - } -} - -// The name of rustc's own place to organize libraries. -// Used to be "rustc", now the default is "rustlib" -const RUST_LIB_DIR: &str = "rustlib"; diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs deleted file mode 100644 index 38ee4deae03..00000000000 --- a/src/librustc/session/mod.rs +++ /dev/null @@ -1,1301 +0,0 @@ -pub use self::code_stats::{DataTypeKind, SizeKind, FieldInfo, VariantInfo}; -use self::code_stats::CodeStats; - -use rustc_session::cgu_reuse_tracker::CguReuseTracker; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - -use rustc_session::lint; -use crate::session::config::{OutputType, PrintRequest, Sanitizer, SwitchWithOptPath}; -use crate::session::search_paths::{PathKind, SearchPath}; -use crate::util::common::{duration_to_secs_str, ErrorReported}; - -use rustc_data_structures::base_n; -use rustc_data_structures::sync::{ - self, Lrc, Lock, OneThread, Once, AtomicU64, AtomicUsize, Ordering, - Ordering::SeqCst, -}; - -use errors::{DiagnosticBuilder, DiagnosticId, Applicability}; -use errors::emitter::{Emitter, EmitterWriter}; -use errors::emitter::HumanReadableErrorType; -use errors::annotate_snippet_emitter_writer::{AnnotateSnippetEmitterWriter}; -use syntax::edition::Edition; -use errors::json::JsonEmitter; -use syntax::source_map; -use syntax::sess::ParseSess; -use syntax_pos::{MultiSpan, Span}; - -use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple}; -use rustc_data_structures::flock; -use rustc_data_structures::jobserver::{self, Client}; -use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; - -use std; -use std::cell::{self, RefCell}; -use std::env; -use std::fmt; -use std::io::Write; -use std::num::NonZeroU32; -use std::path::PathBuf; -use std::time::Duration; -use std::sync::Arc; - -mod code_stats; -pub mod config; -pub mod filesearch; -pub mod search_paths; - -pub struct OptimizationFuel { - /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`. - remaining: u64, - /// We're rejecting all further optimizations. - out_of_fuel: bool, -} - -/// Represents the data associated with a compilation -/// session for a single crate. -pub struct Session { - pub target: config::Config, - pub host: Target, - pub opts: config::Options, - pub host_tlib_path: SearchPath, - /// `None` if the host and target are the same. - pub target_tlib_path: Option, - pub parse_sess: ParseSess, - pub sysroot: PathBuf, - /// The name of the root source file of the crate, in the local file system. - /// `None` means that there is no source file. - pub local_crate_source_file: Option, - /// The directory the compiler has been executed in plus a flag indicating - /// if the value stored here has been affected by path remapping. - pub working_dir: (PathBuf, bool), - - /// Set of `(DiagnosticId, Option, message)` tuples tracking - /// (sub)diagnostics that have been set once, but should not be set again, - /// in order to avoid redundantly verbose output (Issue #24690, #44953). - pub one_time_diagnostics: Lock, String)>>, - pub crate_types: Once>, - /// The `crate_disambiguator` is constructed out of all the `-C metadata` - /// arguments passed to the compiler. Its value together with the crate-name - /// forms a unique global identifier for the crate. It is used to allow - /// multiple crates with the same name to coexist. See the - /// `rustc_codegen_llvm::back::symbol_names` module for more information. - pub crate_disambiguator: Once, - - features: Once, - - /// The maximum recursion limit for potentially infinitely recursive - /// operations such as auto-dereference and monomorphization. - pub recursion_limit: Once, - - /// The maximum length of types during monomorphization. - pub type_length_limit: Once, - - /// Map from imported macro spans (which consist of - /// the localized span for the macro body) to the - /// macro name and definition span in the source crate. - pub imported_macro_spans: OneThread>>, - - incr_comp_session: OneThread>, - /// Used for incremental compilation tests. Will only be populated if - /// `-Zquery-dep-graph` is specified. - pub cgu_reuse_tracker: CguReuseTracker, - - /// Used by `-Z self-profile`. - pub prof: SelfProfilerRef, - - /// Some measurements that are being gathered during compilation. - pub perf_stats: PerfStats, - - /// Data about code being compiled, gathered during compilation. - pub code_stats: CodeStats, - - /// If `-zfuel=crate=n` is specified, `Some(crate)`. - optimization_fuel_crate: Option, - - /// Tracks fuel info if `-zfuel=crate=n` is specified. - optimization_fuel: Lock, - - // The next two are public because the driver needs to read them. - /// If `-zprint-fuel=crate`, `Some(crate)`. - pub print_fuel_crate: Option, - /// Always set to zero and incremented so that we can print fuel expended by a crate. - pub print_fuel: AtomicU64, - - /// Loaded up early on in the initialization of this `Session` to avoid - /// false positives about a job server in our environment. - pub jobserver: Client, - - /// Cap lint level specified by a driver specifically. - pub driver_lint_caps: FxHashMap, - - /// `Span`s of trait methods that weren't found to avoid emitting object safety errors - pub trait_methods_not_found: Lock>, - - /// Mapping from ident span to path span for paths that don't exist as written, but that - /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. - pub confused_type_with_std_module: Lock>, -} - -pub struct PerfStats { - /// The accumulated time spent on computing symbol hashes. - pub symbol_hash_time: Lock, - /// The accumulated time spent decoding def path tables from metadata. - pub decode_def_path_tables_time: Lock, - /// Total number of values canonicalized queries constructed. - pub queries_canonicalized: AtomicUsize, - /// Number of times this query is invoked. - pub normalize_ty_after_erasing_regions: AtomicUsize, - /// Number of times this query is invoked. - pub normalize_projection_ty: AtomicUsize, -} - -/// Enum to support dispatch of one-time diagnostics (in `Session.diag_once`). -enum DiagnosticBuilderMethod { - Note, - SpanNote, - SpanSuggestion(String), // suggestion - // Add more variants as needed to support one-time diagnostics. -} - -/// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid -/// emitting the same message more than once. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum DiagnosticMessageId { - ErrorId(u16), // EXXXX error code as integer - LintId(lint::LintId), - StabilityId(Option), // issue number -} - -impl From<&'static lint::Lint> for DiagnosticMessageId { - fn from(lint: &'static lint::Lint) -> Self { - DiagnosticMessageId::LintId(lint::LintId::of(lint)) - } -} - -impl Session { - pub fn local_crate_disambiguator(&self) -> CrateDisambiguator { - *self.crate_disambiguator.get() - } - - pub fn struct_span_warn>( - &self, - sp: S, - msg: &str, - ) -> DiagnosticBuilder<'_> { - self.diagnostic().struct_span_warn(sp, msg) - } - pub fn struct_span_warn_with_code>( - &self, - sp: S, - msg: &str, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_> { - self.diagnostic().struct_span_warn_with_code(sp, msg, code) - } - pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> { - self.diagnostic().struct_warn(msg) - } - pub fn struct_span_err>( - &self, - sp: S, - msg: &str, - ) -> DiagnosticBuilder<'_> { - self.diagnostic().struct_span_err(sp, msg) - } - pub fn struct_span_err_with_code>( - &self, - sp: S, - msg: &str, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_> { - self.diagnostic().struct_span_err_with_code(sp, msg, code) - } - // FIXME: This method should be removed (every error should have an associated error code). - pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> { - self.diagnostic().struct_err(msg) - } - pub fn struct_err_with_code( - &self, - msg: &str, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_> { - self.diagnostic().struct_err_with_code(msg, code) - } - pub fn struct_span_fatal>( - &self, - sp: S, - msg: &str, - ) -> DiagnosticBuilder<'_> { - self.diagnostic().struct_span_fatal(sp, msg) - } - pub fn struct_span_fatal_with_code>( - &self, - sp: S, - msg: &str, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_> { - self.diagnostic().struct_span_fatal_with_code(sp, msg, code) - } - pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> { - self.diagnostic().struct_fatal(msg) - } - - pub fn span_fatal>(&self, sp: S, msg: &str) -> ! { - self.diagnostic().span_fatal(sp, msg).raise() - } - pub fn span_fatal_with_code>( - &self, - sp: S, - msg: &str, - code: DiagnosticId, - ) -> ! { - self.diagnostic() - .span_fatal_with_code(sp, msg, code) - .raise() - } - pub fn fatal(&self, msg: &str) -> ! { - self.diagnostic().fatal(msg).raise() - } - pub fn span_err_or_warn>(&self, is_warning: bool, sp: S, msg: &str) { - if is_warning { - self.span_warn(sp, msg); - } else { - self.span_err(sp, msg); - } - } - pub fn span_err>(&self, sp: S, msg: &str) { - self.diagnostic().span_err(sp, msg) - } - pub fn span_err_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { - self.diagnostic().span_err_with_code(sp, &msg, code) - } - pub fn err(&self, msg: &str) { - self.diagnostic().err(msg) - } - pub fn err_count(&self) -> usize { - self.diagnostic().err_count() - } - pub fn has_errors(&self) -> bool { - self.diagnostic().has_errors() - } - pub fn has_errors_or_delayed_span_bugs(&self) -> bool { - self.diagnostic().has_errors_or_delayed_span_bugs() - } - pub fn abort_if_errors(&self) { - self.diagnostic().abort_if_errors(); - } - pub fn compile_status(&self) -> Result<(), ErrorReported> { - if self.has_errors() { - self.diagnostic().emit_stashed_diagnostics(); - Err(ErrorReported) - } else { - Ok(()) - } - } - // FIXME(matthewjasper) Remove this method, it should never be needed. - pub fn track_errors(&self, f: F) -> Result - where - F: FnOnce() -> T, - { - let old_count = self.err_count(); - let result = f(); - let errors = self.err_count() - old_count; - if errors == 0 { - Ok(result) - } else { - Err(ErrorReported) - } - } - pub fn span_warn>(&self, sp: S, msg: &str) { - self.diagnostic().span_warn(sp, msg) - } - pub fn span_warn_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { - self.diagnostic().span_warn_with_code(sp, msg, code) - } - pub fn warn(&self, msg: &str) { - self.diagnostic().warn(msg) - } - pub fn opt_span_warn>(&self, opt_sp: Option, msg: &str) { - match opt_sp { - Some(sp) => self.span_warn(sp, msg), - None => self.warn(msg), - } - } - /// Delay a span_bug() call until abort_if_errors() - pub fn delay_span_bug>(&self, sp: S, msg: &str) { - self.diagnostic().delay_span_bug(sp, msg) - } - pub fn note_without_error(&self, msg: &str) { - self.diagnostic().note_without_error(msg) - } - pub fn span_note_without_error>(&self, sp: S, msg: &str) { - self.diagnostic().span_note_without_error(sp, msg) - } - - pub fn diagnostic(&self) -> &errors::Handler { - &self.parse_sess.span_diagnostic - } - - /// Analogous to calling methods on the given `DiagnosticBuilder`, but - /// deduplicates on lint ID, span (if any), and message for this `Session` - fn diag_once<'a, 'b>( - &'a self, - diag_builder: &'b mut DiagnosticBuilder<'a>, - method: DiagnosticBuilderMethod, - msg_id: DiagnosticMessageId, - message: &str, - span_maybe: Option, - ) { - let id_span_message = (msg_id, span_maybe, message.to_owned()); - let fresh = self.one_time_diagnostics - .borrow_mut() - .insert(id_span_message); - if fresh { - match method { - DiagnosticBuilderMethod::Note => { - diag_builder.note(message); - } - DiagnosticBuilderMethod::SpanNote => { - let span = span_maybe.expect("`span_note` needs a span"); - diag_builder.span_note(span, message); - } - DiagnosticBuilderMethod::SpanSuggestion(suggestion) => { - let span = span_maybe.expect("`span_suggestion_*` needs a span"); - diag_builder.span_suggestion( - span, - message, - suggestion, - Applicability::Unspecified, - ); - } - } - } - } - - pub fn diag_span_note_once<'a, 'b>( - &'a self, - diag_builder: &'b mut DiagnosticBuilder<'a>, - msg_id: DiagnosticMessageId, - span: Span, - message: &str, - ) { - self.diag_once( - diag_builder, - DiagnosticBuilderMethod::SpanNote, - msg_id, - message, - Some(span), - ); - } - - pub fn diag_note_once<'a, 'b>( - &'a self, - diag_builder: &'b mut DiagnosticBuilder<'a>, - msg_id: DiagnosticMessageId, - message: &str, - ) { - self.diag_once( - diag_builder, - DiagnosticBuilderMethod::Note, - msg_id, - message, - None, - ); - } - - pub fn diag_span_suggestion_once<'a, 'b>( - &'a self, - diag_builder: &'b mut DiagnosticBuilder<'a>, - msg_id: DiagnosticMessageId, - span: Span, - message: &str, - suggestion: String, - ) { - self.diag_once( - diag_builder, - DiagnosticBuilderMethod::SpanSuggestion(suggestion), - msg_id, - message, - Some(span), - ); - } - - pub fn source_map(&self) -> &source_map::SourceMap { - self.parse_sess.source_map() - } - pub fn verbose(&self) -> bool { - self.opts.debugging_opts.verbose - } - pub fn time_passes(&self) -> bool { - self.opts.debugging_opts.time_passes || self.opts.debugging_opts.time - } - pub fn time_extended(&self) -> bool { - self.opts.debugging_opts.time_passes - } - pub fn instrument_mcount(&self) -> bool { - self.opts.debugging_opts.instrument_mcount - } - pub fn time_llvm_passes(&self) -> bool { - self.opts.debugging_opts.time_llvm_passes - } - pub fn meta_stats(&self) -> bool { - self.opts.debugging_opts.meta_stats - } - pub fn asm_comments(&self) -> bool { - self.opts.debugging_opts.asm_comments - } - pub fn verify_llvm_ir(&self) -> bool { - self.opts.debugging_opts.verify_llvm_ir - || cfg!(always_verify_llvm_ir) - } - pub fn borrowck_stats(&self) -> bool { - self.opts.debugging_opts.borrowck_stats - } - pub fn print_llvm_passes(&self) -> bool { - self.opts.debugging_opts.print_llvm_passes - } - pub fn binary_dep_depinfo(&self) -> bool { - self.opts.debugging_opts.binary_dep_depinfo - } - - /// Gets the features enabled for the current compilation session. - /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents - /// dependency tracking. Use tcx.features() instead. - #[inline] - pub fn features_untracked(&self) -> &rustc_feature::Features { - self.features.get() - } - - pub fn init_features(&self, features: rustc_feature::Features) { - self.features.set(features); - } - - /// Calculates the flavor of LTO to use for this compilation. - pub fn lto(&self) -> config::Lto { - // If our target has codegen requirements ignore the command line - if self.target.target.options.requires_lto { - return config::Lto::Fat; - } - - // If the user specified something, return that. If they only said `-C - // lto` and we've for whatever reason forced off ThinLTO via the CLI, - // then ensure we can't use a ThinLTO. - match self.opts.cg.lto { - config::LtoCli::Unspecified => { - // The compiler was invoked without the `-Clto` flag. Fall - // through to the default handling - } - config::LtoCli::No => { - // The user explicitly opted out of any kind of LTO - return config::Lto::No; - } - config::LtoCli::Yes | - config::LtoCli::Fat | - config::LtoCli::NoParam => { - // All of these mean fat LTO - return config::Lto::Fat; - } - config::LtoCli::Thin => { - return if self.opts.cli_forced_thinlto_off { - config::Lto::Fat - } else { - config::Lto::Thin - }; - } - } - - // Ok at this point the target doesn't require anything and the user - // hasn't asked for anything. Our next decision is whether or not - // we enable "auto" ThinLTO where we use multiple codegen units and - // then do ThinLTO over those codegen units. The logic below will - // either return `No` or `ThinLocal`. - - // If processing command line options determined that we're incompatible - // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option. - if self.opts.cli_forced_thinlto_off { - return config::Lto::No; - } - - // If `-Z thinlto` specified process that, but note that this is mostly - // a deprecated option now that `-C lto=thin` exists. - if let Some(enabled) = self.opts.debugging_opts.thinlto { - if enabled { - return config::Lto::ThinLocal; - } else { - return config::Lto::No; - } - } - - // If there's only one codegen unit and LTO isn't enabled then there's - // no need for ThinLTO so just return false. - if self.codegen_units() == 1 { - return config::Lto::No; - } - - // Now we're in "defaults" territory. By default we enable ThinLTO for - // optimized compiles (anything greater than O0). - match self.opts.optimize { - config::OptLevel::No => config::Lto::No, - _ => config::Lto::ThinLocal, - } - } - - /// Returns the panic strategy for this compile session. If the user explicitly selected one - /// using '-C panic', use that, otherwise use the panic strategy defined by the target. - pub fn panic_strategy(&self) -> PanicStrategy { - self.opts - .cg - .panic - .unwrap_or(self.target.target.options.panic_strategy) - } - pub fn fewer_names(&self) -> bool { - let more_names = self.opts - .output_types - .contains_key(&OutputType::LlvmAssembly) - || self.opts.output_types.contains_key(&OutputType::Bitcode); - - // Address sanitizer and memory sanitizer use alloca name when reporting an issue. - let more_names = match self.opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) => true, - Some(Sanitizer::Memory) => true, - _ => more_names, - }; - - self.opts.debugging_opts.fewer_names || !more_names - } - - pub fn no_landing_pads(&self) -> bool { - self.opts.debugging_opts.no_landing_pads || self.panic_strategy() == PanicStrategy::Abort - } - pub fn unstable_options(&self) -> bool { - self.opts.debugging_opts.unstable_options - } - pub fn overflow_checks(&self) -> bool { - self.opts - .cg - .overflow_checks - .or(self.opts.debugging_opts.force_overflow_checks) - .unwrap_or(self.opts.debug_assertions) - } - - pub fn crt_static(&self) -> bool { - // If the target does not opt in to crt-static support, use its default. - if self.target.target.options.crt_static_respected { - self.crt_static_feature() - } else { - self.target.target.options.crt_static_default - } - } - - pub fn crt_static_feature(&self) -> bool { - let requested_features = self.opts.cg.target_feature.split(','); - let found_negative = requested_features.clone().any(|r| r == "-crt-static"); - let found_positive = requested_features.clone().any(|r| r == "+crt-static"); - - // If the target we're compiling for requests a static crt by default, - // then see if the `-crt-static` feature was passed to disable that. - // Otherwise if we don't have a static crt by default then see if the - // `+crt-static` feature was passed. - if self.target.target.options.crt_static_default { - !found_negative - } else { - found_positive - } - } - - pub fn must_not_eliminate_frame_pointers(&self) -> bool { - // "mcount" function relies on stack pointer. - // See . - if self.instrument_mcount() { - true - } else if let Some(x) = self.opts.cg.force_frame_pointers { - x - } else { - !self.target.target.options.eliminate_frame_pointer - } - } - - /// Returns the symbol name for the registrar function, - /// given the crate `Svh` and the function `DefIndex`. - pub fn generate_plugin_registrar_symbol(&self, disambiguator: CrateDisambiguator) -> String { - format!( - "__rustc_plugin_registrar_{}__", - disambiguator.to_fingerprint().to_hex() - ) - } - - pub fn generate_proc_macro_decls_symbol(&self, disambiguator: CrateDisambiguator) -> String { - format!( - "__rustc_proc_macro_decls_{}__", - disambiguator.to_fingerprint().to_hex() - ) - } - - pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { - filesearch::FileSearch::new( - &self.sysroot, - self.opts.target_triple.triple(), - &self.opts.search_paths, - // `target_tlib_path == None` means it's the same as `host_tlib_path`. - self.target_tlib_path.as_ref().unwrap_or(&self.host_tlib_path), - kind, - ) - } - pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { - filesearch::FileSearch::new( - &self.sysroot, - config::host_triple(), - &self.opts.search_paths, - &self.host_tlib_path, - kind, - ) - } - - pub fn set_incr_session_load_dep_graph(&self, load: bool) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::Active { ref mut load_dep_graph, .. } = *incr_comp_session { - *load_dep_graph = load; - } - } - - pub fn incr_session_load_dep_graph(&self) -> bool { - let incr_comp_session = self.incr_comp_session.borrow(); - match *incr_comp_session { - IncrCompSession::Active { load_dep_graph, .. } => load_dep_graph, - _ => false, - } - } - - pub fn init_incr_comp_session( - &self, - session_dir: PathBuf, - lock_file: flock::Lock, - load_dep_graph: bool, - ) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::NotInitialized = *incr_comp_session { - } else { - bug!( - "Trying to initialize IncrCompSession `{:?}`", - *incr_comp_session - ) - } - - *incr_comp_session = IncrCompSession::Active { - session_directory: session_dir, - lock_file, - load_dep_graph, - }; - } - - pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::Active { .. } = *incr_comp_session { - } else { - bug!( - "trying to finalize `IncrCompSession` `{:?}`", - *incr_comp_session - ); - } - - // Note: this will also drop the lock file, thus unlocking the directory. - *incr_comp_session = IncrCompSession::Finalized { - session_directory: new_directory_path, - }; - } - - pub fn mark_incr_comp_session_as_invalid(&self) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - let session_directory = match *incr_comp_session { - IncrCompSession::Active { - ref session_directory, - .. - } => session_directory.clone(), - IncrCompSession::InvalidBecauseOfErrors { .. } => return, - _ => bug!( - "trying to invalidate `IncrCompSession` `{:?}`", - *incr_comp_session - ), - }; - - // Note: this will also drop the lock file, thus unlocking the directory. - *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { - session_directory, - }; - } - - pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> { - let incr_comp_session = self.incr_comp_session.borrow(); - cell::Ref::map( - incr_comp_session, - |incr_comp_session| match *incr_comp_session { - IncrCompSession::NotInitialized => bug!( - "trying to get session directory from `IncrCompSession`: {:?}", - *incr_comp_session, - ), - IncrCompSession::Active { - ref session_directory, - .. - } - | IncrCompSession::Finalized { - ref session_directory, - } - | IncrCompSession::InvalidBecauseOfErrors { - ref session_directory, - } => session_directory, - }, - ) - } - - pub fn incr_comp_session_dir_opt(&self) -> Option> { - if self.opts.incremental.is_some() { - Some(self.incr_comp_session_dir()) - } else { - None - } - } - - pub fn print_perf_stats(&self) { - println!( - "Total time spent computing symbol hashes: {}", - duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) - ); - println!( - "Total time spent decoding DefPath tables: {}", - duration_to_secs_str(*self.perf_stats.decode_def_path_tables_time.lock()) - ); - println!("Total queries canonicalized: {}", - self.perf_stats.queries_canonicalized.load(Ordering::Relaxed)); - println!("normalize_ty_after_erasing_regions: {}", - self.perf_stats.normalize_ty_after_erasing_regions.load(Ordering::Relaxed)); - println!("normalize_projection_ty: {}", - self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed)); - } - - /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. - /// This expends fuel if applicable, and records fuel if applicable. - pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { - let mut ret = true; - if let Some(ref c) = self.optimization_fuel_crate { - if c == crate_name { - assert_eq!(self.threads(), 1); - let mut fuel = self.optimization_fuel.lock(); - ret = fuel.remaining != 0; - if fuel.remaining == 0 && !fuel.out_of_fuel { - eprintln!("optimization-fuel-exhausted: {}", msg()); - fuel.out_of_fuel = true; - } else if fuel.remaining > 0 { - fuel.remaining -= 1; - } - } - } - if let Some(ref c) = self.print_fuel_crate { - if c == crate_name { - assert_eq!(self.threads(), 1); - self.print_fuel.fetch_add(1, SeqCst); - } - } - ret - } - - /// Returns the number of query threads that should be used for this - /// compilation - pub fn threads(&self) -> usize { - self.opts.debugging_opts.threads - } - - /// Returns the number of codegen units that should be used for this - /// compilation - pub fn codegen_units(&self) -> usize { - if let Some(n) = self.opts.cli_forced_codegen_units { - return n; - } - if let Some(n) = self.target.target.options.default_codegen_units { - return n as usize; - } - - // Why is 16 codegen units the default all the time? - // - // The main reason for enabling multiple codegen units by default is to - // leverage the ability for the codegen backend to do codegen and - // optimization in parallel. This allows us, especially for large crates, to - // make good use of all available resources on the machine once we've - // hit that stage of compilation. Large crates especially then often - // take a long time in codegen/optimization and this helps us amortize that - // cost. - // - // Note that a high number here doesn't mean that we'll be spawning a - // large number of threads in parallel. The backend of rustc contains - // global rate limiting through the `jobserver` crate so we'll never - // overload the system with too much work, but rather we'll only be - // optimizing when we're otherwise cooperating with other instances of - // rustc. - // - // Rather a high number here means that we should be able to keep a lot - // of idle cpus busy. By ensuring that no codegen unit takes *too* long - // to build we'll be guaranteed that all cpus will finish pretty closely - // to one another and we should make relatively optimal use of system - // resources - // - // Note that the main cost of codegen units is that it prevents LLVM - // from inlining across codegen units. Users in general don't have a lot - // of control over how codegen units are split up so it's our job in the - // compiler to ensure that undue performance isn't lost when using - // codegen units (aka we can't require everyone to slap `#[inline]` on - // everything). - // - // If we're compiling at `-O0` then the number doesn't really matter too - // much because performance doesn't matter and inlining is ok to lose. - // In debug mode we just want to try to guarantee that no cpu is stuck - // doing work that could otherwise be farmed to others. - // - // In release mode, however (O1 and above) performance does indeed - // matter! To recover the loss in performance due to inlining we'll be - // enabling ThinLTO by default (the function for which is just below). - // This will ensure that we recover any inlining wins we otherwise lost - // through codegen unit partitioning. - // - // --- - // - // Ok that's a lot of words but the basic tl;dr; is that we want a high - // number here -- but not too high. Additionally we're "safe" to have it - // always at the same number at all optimization levels. - // - // As a result 16 was chosen here! Mostly because it was a power of 2 - // and most benchmarks agreed it was roughly a local optimum. Not very - // scientific. - 16 - } - - pub fn teach(&self, code: &DiagnosticId) -> bool { - self.opts.debugging_opts.teach && self.diagnostic().must_teach(code) - } - - pub fn rust_2015(&self) -> bool { - self.opts.edition == Edition::Edition2015 - } - - /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(&self) -> bool { - self.opts.edition >= Edition::Edition2018 - } - - pub fn edition(&self) -> Edition { - self.opts.edition - } - - /// Returns `true` if we cannot skip the PLT for shared library calls. - pub fn needs_plt(&self) -> bool { - // Check if the current target usually needs PLT to be enabled. - // The user can use the command line flag to override it. - let needs_plt = self.target.target.options.needs_plt; - - let dbg_opts = &self.opts.debugging_opts; - - let relro_level = dbg_opts.relro_level - .unwrap_or(self.target.target.options.relro_level); - - // Only enable this optimization by default if full relro is also enabled. - // In this case, lazy binding was already unavailable, so nothing is lost. - // This also ensures `-Wl,-z,now` is supported by the linker. - let full_relro = RelroLevel::Full == relro_level; - - // If user didn't explicitly forced us to use / skip the PLT, - // then try to skip it where possible. - dbg_opts.plt.unwrap_or(needs_plt || !full_relro) - } -} - -pub fn build_session( - sopts: config::Options, - local_crate_source_file: Option, - registry: errors::registry::Registry, -) -> Session { - let file_path_mapping = sopts.file_path_mapping(); - - build_session_with_source_map( - sopts, - local_crate_source_file, - registry, - Lrc::new(source_map::SourceMap::new(file_path_mapping)), - DiagnosticOutput::Default, - Default::default(), - ) -} - -fn default_emitter( - sopts: &config::Options, - registry: errors::registry::Registry, - source_map: &Lrc, - emitter_dest: Option>, -) -> Box { - let external_macro_backtrace = sopts.debugging_opts.external_macro_backtrace; - match (sopts.error_format, emitter_dest) { - (config::ErrorOutputType::HumanReadable(kind), dst) => { - let (short, color_config) = kind.unzip(); - - if let HumanReadableErrorType::AnnotateSnippet(_) = kind { - let emitter = AnnotateSnippetEmitterWriter::new( - Some(source_map.clone()), - short, - external_macro_backtrace, - ); - Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) - } else { - let emitter = match dst { - None => EmitterWriter::stderr( - color_config, - Some(source_map.clone()), - short, - sopts.debugging_opts.teach, - sopts.debugging_opts.terminal_width, - external_macro_backtrace, - ), - Some(dst) => EmitterWriter::new( - dst, - Some(source_map.clone()), - short, - false, // no teach messages when writing to a buffer - false, // no colors when writing to a buffer - None, // no terminal width - external_macro_backtrace, - ), - }; - Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) - } - }, - (config::ErrorOutputType::Json { pretty, json_rendered }, None) => Box::new( - JsonEmitter::stderr( - Some(registry), - source_map.clone(), - pretty, - json_rendered, - external_macro_backtrace, - ).ui_testing(sopts.debugging_opts.ui_testing), - ), - (config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new( - JsonEmitter::new( - dst, - Some(registry), - source_map.clone(), - pretty, - json_rendered, - external_macro_backtrace, - ).ui_testing(sopts.debugging_opts.ui_testing), - ), - } -} - -pub enum DiagnosticOutput { - Default, - Raw(Box) -} - -pub fn build_session_with_source_map( - sopts: config::Options, - local_crate_source_file: Option, - registry: errors::registry::Registry, - source_map: Lrc, - diagnostics_output: DiagnosticOutput, - lint_caps: FxHashMap, -) -> Session { - // FIXME: This is not general enough to make the warning lint completely override - // normal diagnostic warnings, since the warning lint can also be denied and changed - // later via the source code. - let warnings_allow = sopts - .lint_opts - .iter() - .filter(|&&(ref key, _)| *key == "warnings") - .map(|&(_, ref level)| *level == lint::Allow) - .last() - .unwrap_or(false); - let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow); - - let can_emit_warnings = !(warnings_allow || cap_lints_allow); - - let treat_err_as_bug = sopts.debugging_opts.treat_err_as_bug; - let dont_buffer_diagnostics = sopts.debugging_opts.dont_buffer_diagnostics; - let report_delayed_bugs = sopts.debugging_opts.report_delayed_bugs; - - let external_macro_backtrace = sopts.debugging_opts.external_macro_backtrace; - - let write_dest = match diagnostics_output { - DiagnosticOutput::Default => None, - DiagnosticOutput::Raw(write) => Some(write), - }; - let emitter = default_emitter(&sopts, registry, &source_map, write_dest); - - let diagnostic_handler = errors::Handler::with_emitter_and_flags( - emitter, - errors::HandlerFlags { - can_emit_warnings, - treat_err_as_bug, - report_delayed_bugs, - dont_buffer_diagnostics, - external_macro_backtrace, - ..Default::default() - }, - ); - - build_session_( - sopts, - local_crate_source_file, - diagnostic_handler, - source_map, - lint_caps, - ) -} - -fn build_session_( - sopts: config::Options, - local_crate_source_file: Option, - span_diagnostic: errors::Handler, - source_map: Lrc, - driver_lint_caps: FxHashMap, -) -> Session { - let self_profiler = - if let SwitchWithOptPath::Enabled(ref d) = sopts.debugging_opts.self_profile { - let directory = if let Some(ref directory) = d { - directory - } else { - std::path::Path::new(".") - }; - - let profiler = SelfProfiler::new( - directory, - sopts.crate_name.as_ref().map(|s| &s[..]), - &sopts.debugging_opts.self_profile_events - ); - match profiler { - Ok(profiler) => { - Some(Arc::new(profiler)) - }, - Err(e) => { - early_warn(sopts.error_format, &format!("failed to create profiler: {}", e)); - None - } - } - } - else { None }; - - let host_triple = TargetTriple::from_triple(config::host_triple()); - let host = Target::search(&host_triple).unwrap_or_else(|e| - span_diagnostic - .fatal(&format!("Error loading host specification: {}", e)) - .raise() - ); - let target_cfg = config::build_target_config(&sopts, &span_diagnostic); - - let parse_sess = ParseSess::with_span_handler( - span_diagnostic, - source_map, - ); - let sysroot = match &sopts.maybe_sysroot { - Some(sysroot) => sysroot.clone(), - None => filesearch::get_or_default_sysroot(), - }; - - let host_triple = config::host_triple(); - let target_triple = sopts.target_triple.triple(); - let host_tlib_path = SearchPath::from_sysroot_and_triple(&sysroot, host_triple); - let target_tlib_path = if host_triple == target_triple { - None - } else { - Some(SearchPath::from_sysroot_and_triple(&sysroot, target_triple)) - }; - - let file_path_mapping = sopts.file_path_mapping(); - - let local_crate_source_file = - local_crate_source_file.map(|path| file_path_mapping.map_prefix(path).0); - - let optimization_fuel_crate = sopts.debugging_opts.fuel.as_ref().map(|i| i.0.clone()); - let optimization_fuel = Lock::new(OptimizationFuel { - remaining: sopts.debugging_opts.fuel.as_ref().map(|i| i.1).unwrap_or(0), - out_of_fuel: false, - }); - let print_fuel_crate = sopts.debugging_opts.print_fuel.clone(); - let print_fuel = AtomicU64::new(0); - - let working_dir = env::current_dir().unwrap_or_else(|e| - parse_sess.span_diagnostic - .fatal(&format!("Current directory is invalid: {}", e)) - .raise() - ); - let working_dir = file_path_mapping.map_prefix(working_dir); - - let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph { - CguReuseTracker::new() - } else { - CguReuseTracker::new_disabled() - }; - - let sess = Session { - target: target_cfg, - host, - opts: sopts, - host_tlib_path, - target_tlib_path, - parse_sess, - sysroot, - local_crate_source_file, - working_dir, - one_time_diagnostics: Default::default(), - crate_types: Once::new(), - crate_disambiguator: Once::new(), - features: Once::new(), - recursion_limit: Once::new(), - type_length_limit: Once::new(), - imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())), - incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), - cgu_reuse_tracker, - prof: SelfProfilerRef::new(self_profiler), - perf_stats: PerfStats { - symbol_hash_time: Lock::new(Duration::from_secs(0)), - decode_def_path_tables_time: Lock::new(Duration::from_secs(0)), - queries_canonicalized: AtomicUsize::new(0), - normalize_ty_after_erasing_regions: AtomicUsize::new(0), - normalize_projection_ty: AtomicUsize::new(0), - }, - code_stats: Default::default(), - optimization_fuel_crate, - optimization_fuel, - print_fuel_crate, - print_fuel, - jobserver: jobserver::client(), - driver_lint_caps, - trait_methods_not_found: Lock::new(Default::default()), - confused_type_with_std_module: Lock::new(Default::default()), - }; - - validate_commandline_args_with_session_available(&sess); - - sess -} - -// If it is useful to have a Session available already for validating a -// commandline argument, you can do so here. -fn validate_commandline_args_with_session_available(sess: &Session) { - // Since we don't know if code in an rlib will be linked to statically or - // dynamically downstream, rustc generates `__imp_` symbols that help the - // MSVC linker deal with this lack of knowledge (#27438). Unfortunately, - // these manually generated symbols confuse LLD when it tries to merge - // bitcode during ThinLTO. Therefore we disallow dynamic linking on MSVC - // when compiling for LLD ThinLTO. This way we can validly just not generate - // the `dllimport` attributes and `__imp_` symbols in that case. - if sess.opts.cg.linker_plugin_lto.enabled() && - sess.opts.cg.prefer_dynamic && - sess.target.target.options.is_like_msvc { - sess.err("Linker plugin based LTO is not supported together with \ - `-C prefer-dynamic` when targeting MSVC"); - } - - // Make sure that any given profiling data actually exists so LLVM can't - // decide to silently skip PGO. - if let Some(ref path) = sess.opts.cg.profile_use { - if !path.exists() { - sess.err(&format!("File `{}` passed to `-C profile-use` does not exist.", - path.display())); - } - } - - // PGO does not work reliably with panic=unwind on Windows. Let's make it - // an error to combine the two for now. It always runs into an assertions - // if LLVM is built with assertions, but without assertions it sometimes - // does not crash and will probably generate a corrupted binary. - // We should only display this error if we're actually going to run PGO. - // If we're just supposed to print out some data, don't show the error (#61002). - if sess.opts.cg.profile_generate.enabled() && - sess.target.target.options.is_like_msvc && - sess.panic_strategy() == PanicStrategy::Unwind && - sess.opts.prints.iter().all(|&p| p == PrintRequest::NativeStaticLibs) { - sess.err("Profile-guided optimization does not yet work in conjunction \ - with `-Cpanic=unwind` on Windows when targeting MSVC. \ - See https://github.com/rust-lang/rust/issues/61002 for details."); - } -} - -/// Hash value constructed out of all the `-C metadata` arguments passed to the -/// compiler. Together with the crate-name forms a unique global identifier for -/// the crate. -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, RustcEncodable, RustcDecodable)] -pub struct CrateDisambiguator(Fingerprint); - -impl CrateDisambiguator { - pub fn to_fingerprint(self) -> Fingerprint { - self.0 - } -} - -impl fmt::Display for CrateDisambiguator { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - let (a, b) = self.0.as_value(); - let as_u128 = a as u128 | ((b as u128) << 64); - f.write_str(&base_n::encode(as_u128, base_n::CASE_INSENSITIVE)) - } -} - -impl From for CrateDisambiguator { - fn from(fingerprint: Fingerprint) -> CrateDisambiguator { - CrateDisambiguator(fingerprint) - } -} - -impl_stable_hash_via_hash!(CrateDisambiguator); - -/// Holds data on the current incremental compilation session, if there is one. -#[derive(Debug)] -pub enum IncrCompSession { - /// This is the state the session will be in until the incr. comp. dir is - /// needed. - NotInitialized, - /// This is the state during which the session directory is private and can - /// be modified. - Active { - session_directory: PathBuf, - lock_file: flock::Lock, - load_dep_graph: bool, - }, - /// This is the state after the session directory has been finalized. In this - /// state, the contents of the directory must not be modified any more. - Finalized { session_directory: PathBuf }, - /// This is an error state that is reached when some compilation error has - /// occurred. It indicates that the contents of the session directory must - /// not be used, since they might be invalid. - InvalidBecauseOfErrors { session_directory: PathBuf }, -} - -pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { - let emitter: Box = match output { - config::ErrorOutputType::HumanReadable(kind) => { - let (short, color_config) = kind.unzip(); - Box::new(EmitterWriter::stderr(color_config, None, short, false, None, false)) - } - config::ErrorOutputType::Json { pretty, json_rendered } => - Box::new(JsonEmitter::basic(pretty, json_rendered, false)), - }; - let handler = errors::Handler::with_emitter(true, None, emitter); - handler.struct_fatal(msg).emit(); - errors::FatalError.raise(); -} - -pub fn early_warn(output: config::ErrorOutputType, msg: &str) { - let emitter: Box = match output { - config::ErrorOutputType::HumanReadable(kind) => { - let (short, color_config) = kind.unzip(); - Box::new(EmitterWriter::stderr(color_config, None, short, false, None, false)) - } - config::ErrorOutputType::Json { pretty, json_rendered } => - Box::new(JsonEmitter::basic(pretty, json_rendered, false)), - }; - let handler = errors::Handler::with_emitter(true, None, emitter); - handler.struct_warn(msg).emit(); -} - -pub type CompileResult = Result<(), ErrorReported>; diff --git a/src/librustc/session/search_paths.rs b/src/librustc/session/search_paths.rs deleted file mode 100644 index 949dad751a1..00000000000 --- a/src/librustc/session/search_paths.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::path::{Path, PathBuf}; -use crate::session::{early_error, config}; -use crate::session::filesearch::make_target_lib_path; - -#[derive(Clone, Debug)] -pub struct SearchPath { - pub kind: PathKind, - pub dir: PathBuf, - pub files: Vec, -} - -#[derive(PartialEq, Clone, Copy, Debug, HashStable)] -pub enum PathKind { - Native, - Crate, - Dependency, - Framework, - ExternFlag, - All, -} - -impl PathKind { - pub fn matches(&self, kind: PathKind) -> bool { - match (self, kind) { - (PathKind::All, _) | (_, PathKind::All) => true, - _ => *self == kind, - } - } -} - -impl SearchPath { - pub fn from_cli_opt(path: &str, output: config::ErrorOutputType) -> Self { - let (kind, path) = if path.starts_with("native=") { - (PathKind::Native, &path["native=".len()..]) - } else if path.starts_with("crate=") { - (PathKind::Crate, &path["crate=".len()..]) - } else if path.starts_with("dependency=") { - (PathKind::Dependency, &path["dependency=".len()..]) - } else if path.starts_with("framework=") { - (PathKind::Framework, &path["framework=".len()..]) - } else if path.starts_with("all=") { - (PathKind::All, &path["all=".len()..]) - } else { - (PathKind::All, path) - }; - if path.is_empty() { - early_error(output, "empty search path given via `-L`"); - } - - let dir = PathBuf::from(path); - Self::new(kind, dir) - } - - pub fn from_sysroot_and_triple(sysroot: &Path, triple: &str) -> Self { - Self::new(PathKind::All, make_target_lib_path(sysroot, triple)) - } - - fn new(kind: PathKind, dir: PathBuf) -> Self { - // Get the files within the directory. - let files = match std::fs::read_dir(&dir) { - Ok(files) => { - files.filter_map(|p| { - p.ok().map(|s| s.path()) - }) - .collect::>() - } - Err(..) => vec![], - }; - - SearchPath { kind, dir, files } - } -} diff --git a/src/librustc_session/Cargo.toml b/src/librustc_session/Cargo.toml index f90ba129a45..dbbb4b92dea 100644 --- a/src/librustc_session/Cargo.toml +++ b/src/librustc_session/Cargo.toml @@ -11,7 +11,11 @@ path = "lib.rs" [dependencies] log = "0.4" rustc_errors = { path = "../librustc_errors" } +rustc_feature = { path = "../librustc_feature" } +rustc_target = { path = "../librustc_target" } rustc_serialize = { path = "../libserialize", package = "serialize" } rustc_data_structures = { path = "../librustc_data_structures" } syntax_pos = { path = "../libsyntax_pos" } rustc_index = { path = "../librustc_index" } +rustc_fs_util = { path = "../librustc_fs_util" } +num_cpus = "1.0" diff --git a/src/librustc_session/code_stats.rs b/src/librustc_session/code_stats.rs new file mode 100644 index 00000000000..5baf0c5948f --- /dev/null +++ b/src/librustc_session/code_stats.rs @@ -0,0 +1,177 @@ +use rustc_target::abi::{Align, Size}; +use rustc_data_structures::fx::{FxHashSet}; +use std::cmp::{self, Ordering}; +use rustc_data_structures::sync::Lock; + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct VariantInfo { + pub name: Option, + pub kind: SizeKind, + pub size: u64, + pub align: u64, + pub fields: Vec, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum SizeKind { + Exact, + Min, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct FieldInfo { + pub name: String, + pub offset: u64, + pub size: u64, + pub align: u64, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum DataTypeKind { + Struct, + Union, + Enum, + Closure, +} + +#[derive(PartialEq, Eq, Hash, Debug)] +pub struct TypeSizeInfo { + pub kind: DataTypeKind, + pub type_description: String, + pub align: u64, + pub overall_size: u64, + pub packed: bool, + pub opt_discr_size: Option, + pub variants: Vec, +} + +#[derive(Default)] +pub struct CodeStats { + type_sizes: Lock>, +} + +impl CodeStats { + pub fn record_type_size(&self, + kind: DataTypeKind, + type_desc: S, + align: Align, + overall_size: Size, + packed: bool, + opt_discr_size: Option, + mut variants: Vec) { + // Sort variants so the largest ones are shown first. A stable sort is + // used here so that source code order is preserved for all variants + // that have the same size. + variants.sort_by(|info1, info2| { + info2.size.cmp(&info1.size) + }); + let info = TypeSizeInfo { + kind, + type_description: type_desc.to_string(), + align: align.bytes(), + overall_size: overall_size.bytes(), + packed: packed, + opt_discr_size: opt_discr_size.map(|s| s.bytes()), + variants, + }; + self.type_sizes.borrow_mut().insert(info); + } + + pub fn print_type_sizes(&self) { + let type_sizes = self.type_sizes.borrow(); + let mut sorted: Vec<_> = type_sizes.iter().collect(); + + // Primary sort: large-to-small. + // Secondary sort: description (dictionary order) + sorted.sort_by(|info1, info2| { + // (reversing cmp order to get large-to-small ordering) + match info2.overall_size.cmp(&info1.overall_size) { + Ordering::Equal => info1.type_description.cmp(&info2.type_description), + other => other, + } + }); + + for info in &sorted { + println!("print-type-size type: `{}`: {} bytes, alignment: {} bytes", + info.type_description, info.overall_size, info.align); + let indent = " "; + + let discr_size = if let Some(discr_size) = info.opt_discr_size { + println!("print-type-size {}discriminant: {} bytes", + indent, discr_size); + discr_size + } else { + 0 + }; + + // We start this at discr_size (rather than 0) because + // things like C-enums do not have variants but we still + // want the max_variant_size at the end of the loop below + // to reflect the presence of the discriminant. + let mut max_variant_size = discr_size; + + let struct_like = match info.kind { + DataTypeKind::Struct | DataTypeKind::Closure => true, + DataTypeKind::Enum | DataTypeKind::Union => false, + }; + for (i, variant_info) in info.variants.iter().enumerate() { + let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info; + let indent = if !struct_like { + let name = match name.as_ref() { + Some(name) => name.to_owned(), + None => i.to_string(), + }; + println!("print-type-size {}variant `{}`: {} bytes", + indent, name, size - discr_size); + " " + } else { + assert!(i < 1); + " " + }; + max_variant_size = cmp::max(max_variant_size, size); + + let mut min_offset = discr_size; + + // We want to print fields by increasing offset. + let mut fields = fields.clone(); + fields.sort_by_key(|f| f.offset); + + for field in fields.iter() { + let FieldInfo { ref name, offset, size, align } = *field; + + if offset > min_offset { + let pad = offset - min_offset; + println!("print-type-size {}padding: {} bytes", + indent, pad); + } + + if offset < min_offset { + // if this happens something is very wrong + println!("print-type-size {}field `.{}`: {} bytes, \ + offset: {} bytes, \ + alignment: {} bytes", + indent, name, size, offset, align); + } else if info.packed || offset == min_offset { + println!("print-type-size {}field `.{}`: {} bytes", + indent, name, size); + } else { + // Include field alignment in output only if it caused padding injection + println!("print-type-size {}field `.{}`: {} bytes, \ + alignment: {} bytes", + indent, name, size, align); + } + + min_offset = offset + size; + } + } + + assert!(max_variant_size <= info.overall_size, + "max_variant_size {} !<= {} overall_size", + max_variant_size, info.overall_size); + if max_variant_size < info.overall_size { + println!("print-type-size {}end padding: {} bytes", + indent, info.overall_size - max_variant_size); + } + } + } +} diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs new file mode 100644 index 00000000000..c05704dbab5 --- /dev/null +++ b/src/librustc_session/config.rs @@ -0,0 +1,2998 @@ +//! Contains infrastructure for configuring the compiler, including parsing +//! command-line options. + +use crate::lint; +use crate::utils::NativeLibraryKind; +use crate::{early_error, early_warn, Session}; +use crate::search_paths::SearchPath; + +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::impl_stable_hash_via_hash; + +use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; +use rustc_target::spec::{Target, TargetTriple}; + +// Duplicated from syntax::ast for now +type CrateConfig = FxHashSet<(Symbol, Option)>; + +use syntax_pos::source_map::{FileName, FilePathMapping}; +use syntax_pos::edition::{Edition, EDITION_NAME_LIST, DEFAULT_EDITION}; +use syntax_pos::symbol::{sym, Symbol}; +use rustc_feature::UnstableFeatures; + +use rustc_errors::emitter::HumanReadableErrorType; +use rustc_errors::{ColorConfig, FatalError, Handler}; + +use getopts; + +use std::collections::{BTreeMap, BTreeSet}; +use std::collections::btree_map::{ + Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, +}; +use std::fmt; +use std::str::{self, FromStr}; +use std::hash::Hasher; +use std::collections::hash_map::DefaultHasher; +use std::iter::FromIterator; +use std::path::{Path, PathBuf}; + +pub struct Config { + pub target: Target, + pub ptr_width: u32, +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum Sanitizer { + Address, + Leak, + Memory, + Thread, +} + +impl fmt::Display for Sanitizer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Sanitizer::Address => "address".fmt(f), + Sanitizer::Leak => "leak".fmt(f), + Sanitizer::Memory => "memory".fmt(f), + Sanitizer::Thread => "thread".fmt(f), + } + } +} + +impl FromStr for Sanitizer { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "address" => Ok(Sanitizer::Address), + "leak" => Ok(Sanitizer::Leak), + "memory" => Ok(Sanitizer::Memory), + "thread" => Ok(Sanitizer::Thread), + _ => Err(()), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum OptLevel { + No, // -O0 + Less, // -O1 + Default, // -O2 + Aggressive, // -O3 + Size, // -Os + SizeMin, // -Oz +} + +impl_stable_hash_via_hash!(OptLevel); + +/// This is what the `LtoCli` values get mapped to after resolving defaults and +/// and taking other command line options into account. +#[derive(Clone, PartialEq)] +pub enum Lto { + /// Don't do any LTO whatsoever + No, + + /// Do a full crate graph LTO with ThinLTO + Thin, + + /// Do a local graph LTO with ThinLTO (only relevant for multiple codegen + /// units). + ThinLocal, + + /// Do a full crate graph LTO with "fat" LTO + Fat, +} + +/// The different settings that the `-C lto` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum LtoCli { + /// `-C lto=no` + No, + /// `-C lto=yes` + Yes, + /// `-C lto` + NoParam, + /// `-C lto=thin` + Thin, + /// `-C lto=fat` + Fat, + /// No `-C lto` flag passed + Unspecified, +} + +#[derive(Clone, PartialEq, Hash)] +pub enum LinkerPluginLto { + LinkerPlugin(PathBuf), + LinkerPluginAuto, + Disabled +} + +impl LinkerPluginLto { + pub fn enabled(&self) -> bool { + match *self { + LinkerPluginLto::LinkerPlugin(_) | + LinkerPluginLto::LinkerPluginAuto => true, + LinkerPluginLto::Disabled => false, + } + } +} + +#[derive(Clone, PartialEq, Hash)] +pub enum SwitchWithOptPath { + Enabled(Option), + Disabled, +} + +impl SwitchWithOptPath { + pub fn enabled(&self) -> bool { + match *self { + SwitchWithOptPath::Enabled(_) => true, + SwitchWithOptPath::Disabled => false, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +pub enum SymbolManglingVersion { + Legacy, + V0, +} + +impl_stable_hash_via_hash!(SymbolManglingVersion); + +#[derive(Clone, Copy, PartialEq, Hash)] +pub enum DebugInfo { + None, + Limited, + Full, +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, RustcEncodable, RustcDecodable)] +pub enum OutputType { + Bitcode, + Assembly, + LlvmAssembly, + Mir, + Metadata, + Object, + Exe, + DepInfo, +} + +impl_stable_hash_via_hash!(OutputType); + +impl OutputType { + fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool { + match *self { + OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true, + OutputType::Bitcode + | OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::Object => false, + } + } + + fn shorthand(&self) -> &'static str { + match *self { + OutputType::Bitcode => "llvm-bc", + OutputType::Assembly => "asm", + OutputType::LlvmAssembly => "llvm-ir", + OutputType::Mir => "mir", + OutputType::Object => "obj", + OutputType::Metadata => "metadata", + OutputType::Exe => "link", + OutputType::DepInfo => "dep-info", + } + } + + fn from_shorthand(shorthand: &str) -> Option { + Some(match shorthand { + "asm" => OutputType::Assembly, + "llvm-ir" => OutputType::LlvmAssembly, + "mir" => OutputType::Mir, + "llvm-bc" => OutputType::Bitcode, + "obj" => OutputType::Object, + "metadata" => OutputType::Metadata, + "link" => OutputType::Exe, + "dep-info" => OutputType::DepInfo, + _ => return None, + }) + } + + fn shorthands_display() -> String { + format!( + "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", + OutputType::Bitcode.shorthand(), + OutputType::Assembly.shorthand(), + OutputType::LlvmAssembly.shorthand(), + OutputType::Mir.shorthand(), + OutputType::Object.shorthand(), + OutputType::Metadata.shorthand(), + OutputType::Exe.shorthand(), + OutputType::DepInfo.shorthand(), + ) + } + + pub fn extension(&self) -> &'static str { + match *self { + OutputType::Bitcode => "bc", + OutputType::Assembly => "s", + OutputType::LlvmAssembly => "ll", + OutputType::Mir => "mir", + OutputType::Object => "o", + OutputType::Metadata => "rmeta", + OutputType::DepInfo => "d", + OutputType::Exe => "", + } + } +} + +/// The type of diagnostics output to generate. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ErrorOutputType { + /// Output meant for the consumption of humans. + HumanReadable(HumanReadableErrorType), + /// Output that's consumed by other tools such as `rustfix` or the `RLS`. + Json { + /// Render the JSON in a human readable way (with indents and newlines). + pretty: bool, + /// The JSON output includes a `rendered` field that includes the rendered + /// human output. + json_rendered: HumanReadableErrorType, + }, +} + +impl Default for ErrorOutputType { + fn default() -> Self { + Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto)) + } +} + +/// Use tree-based collections to cheaply get a deterministic `Hash` implementation. +/// *Do not* switch `BTreeMap` out for an unsorted container type! That would break +/// dependency tracking for command-line arguments. +#[derive(Clone, Hash)] +pub struct OutputTypes(BTreeMap>); + +impl_stable_hash_via_hash!(OutputTypes); + +impl OutputTypes { + pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { + OutputTypes(BTreeMap::from_iter( + entries.iter().map(|&(k, ref v)| (k, v.clone())), + )) + } + + pub fn get(&self, key: &OutputType) -> Option<&Option> { + self.0.get(key) + } + + pub fn contains_key(&self, key: &OutputType) -> bool { + self.0.contains_key(key) + } + + pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option> { + self.0.keys() + } + + pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option> { + self.0.values() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + // Returns `true` if any of the output types require codegen or linking. + pub fn should_codegen(&self) -> bool { + self.0.keys().any(|k| match *k { + OutputType::Bitcode + | OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::Object + | OutputType::Exe => true, + OutputType::Metadata | OutputType::DepInfo => false, + }) + } +} + +/// Use tree-based collections to cheaply get a deterministic `Hash` implementation. +/// *Do not* switch `BTreeMap` or `BTreeSet` out for an unsorted container type! That +/// would break dependency tracking for command-line arguments. +#[derive(Clone)] +pub struct Externs(BTreeMap); + +#[derive(Clone, Debug, Default)] +pub struct ExternEntry { + pub locations: BTreeSet>, + pub is_private_dep: bool +} + +impl Externs { + pub fn new(data: BTreeMap) -> Externs { + Externs(data) + } + + pub fn get(&self, key: &str) -> Option<&ExternEntry> { + self.0.get(key) + } + + pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> { + self.0.iter() + } +} + + +macro_rules! hash_option { + ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [UNTRACKED]) => ({}); + ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [TRACKED]) => ({ + if $sub_hashes.insert(stringify!($opt_name), + $opt_expr as &dyn dep_tracking::DepTrackingHash).is_some() { + panic!("duplicate key in CLI DepTrackingHash: {}", stringify!($opt_name)) + } + }); +} + +macro_rules! top_level_options { + (pub struct Options { $( + $opt:ident : $t:ty [$dep_tracking_marker:ident $($warn_val:expr, $warn_text:expr)*], + )* } ) => ( + #[derive(Clone)] + pub struct Options { + $(pub $opt: $t),* + } + + impl Options { + pub fn dep_tracking_hash(&self) -> u64 { + let mut sub_hashes = BTreeMap::new(); + $({ + hash_option!($opt, + &self.$opt, + &mut sub_hashes, + [$dep_tracking_marker $($warn_val, + $warn_text, + self.error_format)*]); + })* + let mut hasher = DefaultHasher::new(); + dep_tracking::stable_hash(sub_hashes, + &mut hasher, + self.error_format); + hasher.finish() + } + } + ); +} + +// The top-level command-line options struct. +// +// For each option, one has to specify how it behaves with regard to the +// dependency tracking system of incremental compilation. This is done via the +// square-bracketed directive after the field type. The options are: +// +// [TRACKED] +// A change in the given field will cause the compiler to completely clear the +// incremental compilation cache before proceeding. +// +// [UNTRACKED] +// Incremental compilation is not influenced by this option. +// +// If you add a new option to this struct or one of the sub-structs like +// `CodegenOptions`, think about how it influences incremental compilation. If in +// doubt, specify [TRACKED], which is always "correct" but might lead to +// unnecessary re-compilation. +top_level_options!( + pub struct Options { + // The crate config requested for the session, which may be combined + // with additional crate configurations during the compile process. + crate_types: Vec [TRACKED], + optimize: OptLevel [TRACKED], + // Include the `debug_assertions` flag in dependency tracking, since it + // can influence whether overflow checks are done or not. + debug_assertions: bool [TRACKED], + debuginfo: DebugInfo [TRACKED], + lint_opts: Vec<(String, lint::Level)> [TRACKED], + lint_cap: Option [TRACKED], + describe_lints: bool [UNTRACKED], + output_types: OutputTypes [TRACKED], + search_paths: Vec [UNTRACKED], + libs: Vec<(String, Option, Option)> [TRACKED], + maybe_sysroot: Option [UNTRACKED], + + target_triple: TargetTriple [TRACKED], + + test: bool [TRACKED], + error_format: ErrorOutputType [UNTRACKED], + + // If `Some`, enable incremental compilation, using the given + // directory to store intermediate results. + incremental: Option [UNTRACKED], + + debugging_opts: DebuggingOptions [TRACKED], + prints: Vec [UNTRACKED], + // Determines which borrow checker(s) to run. This is the parsed, sanitized + // version of `debugging_opts.borrowck`, which is just a plain string. + borrowck_mode: BorrowckMode [UNTRACKED], + cg: CodegenOptions [TRACKED], + externs: Externs [UNTRACKED], + crate_name: Option [TRACKED], + // An optional name to use as the crate for std during std injection, + // written `extern crate name as std`. Defaults to `std`. Used by + // out-of-tree drivers. + alt_std_name: Option [TRACKED], + // Indicates how the compiler should treat unstable features. + unstable_features: UnstableFeatures [TRACKED], + + // Indicates whether this run of the compiler is actually rustdoc. This + // is currently just a hack and will be removed eventually, so please + // try to not rely on this too much. + actually_rustdoc: bool [TRACKED], + + // Specifications of codegen units / ThinLTO which are forced as a + // result of parsing command line options. These are not necessarily + // what rustc was invoked with, but massaged a bit to agree with + // commands like `--emit llvm-ir` which they're often incompatible with + // if we otherwise use the defaults of rustc. + cli_forced_codegen_units: Option [UNTRACKED], + cli_forced_thinlto_off: bool [UNTRACKED], + + // Remap source path prefixes in all output (messages, object files, debug, etc.). + remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED], + + edition: Edition [TRACKED], + + // `true` if we're emitting JSON blobs about each artifact produced + // by the compiler. + json_artifact_notifications: bool [TRACKED], + + pretty: Option [UNTRACKED], + } +); + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum PrintRequest { + FileNames, + Sysroot, + CrateName, + Cfg, + TargetList, + TargetCPUs, + TargetFeatures, + RelocationModels, + CodeModels, + TlsModels, + TargetSpec, + NativeStaticLibs, +} + +#[derive(Copy, Clone)] +pub enum BorrowckMode { + Mir, + Migrate, +} + +impl BorrowckMode { + /// Returns whether we should run the MIR-based borrow check, but also fall back + /// on the AST borrow check if the MIR-based one errors. + pub fn migrate(self) -> bool { + match self { + BorrowckMode::Mir => false, + BorrowckMode::Migrate => true, + } + } +} + +pub enum Input { + /// Load source code from a file. + File(PathBuf), + /// Load source code from a string. + Str { + /// A string that is shown in place of a filename. + name: FileName, + /// An anonymous string containing the source code. + input: String, + }, +} + +impl Input { + pub fn filestem(&self) -> &str { + match *self { + Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(), + Input::Str { .. } => "rust_out", + } + } + + pub fn get_input(&mut self) -> Option<&mut String> { + match *self { + Input::File(_) => None, + Input::Str { ref mut input, .. } => Some(input), + } + } + + pub fn source_name(&self) -> FileName { + match *self { + Input::File(ref ifile) => ifile.clone().into(), + Input::Str { ref name, .. } => name.clone(), + } + } +} + +#[derive(Clone, Hash)] +pub struct OutputFilenames { + pub out_directory: PathBuf, + pub out_filestem: String, + pub single_output_file: Option, + pub extra: String, + pub outputs: OutputTypes, +} + +impl_stable_hash_via_hash!(OutputFilenames); + +pub const RUST_CGU_EXT: &str = "rcgu"; + +impl OutputFilenames { + pub fn path(&self, flavor: OutputType) -> PathBuf { + self.outputs + .get(&flavor) + .and_then(|p| p.to_owned()) + .or_else(|| self.single_output_file.clone()) + .unwrap_or_else(|| self.temp_path(flavor, None)) + } + + /// Gets the path where a compilation artifact of the given type for the + /// given codegen unit should be placed on disk. If codegen_unit_name is + /// None, a path distinct from those of any codegen unit will be generated. + pub fn temp_path(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf { + let extension = flavor.extension(); + self.temp_path_ext(extension, codegen_unit_name) + } + + /// Like temp_path, but also supports things where there is no corresponding + /// OutputType, like noopt-bitcode or lto-bitcode. + pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf { + let base = self.out_directory.join(&self.filestem()); + + let mut extension = String::new(); + + if let Some(codegen_unit_name) = codegen_unit_name { + extension.push_str(codegen_unit_name); + } + + if !ext.is_empty() { + if !extension.is_empty() { + extension.push_str("."); + extension.push_str(RUST_CGU_EXT); + extension.push_str("."); + } + + extension.push_str(ext); + } + + let path = base.with_extension(&extension[..]); + path + } + + pub fn with_extension(&self, extension: &str) -> PathBuf { + self.out_directory + .join(&self.filestem()) + .with_extension(extension) + } + + pub fn filestem(&self) -> String { + format!("{}{}", self.out_filestem, self.extra) + } +} + +pub fn host_triple() -> &'static str { + // Get the host triple out of the build environment. This ensures that our + // idea of the host triple is the same as for the set of libraries we've + // actually built. We can't just take LLVM's host triple because they + // normalize all ix86 architectures to i386. + // + // Instead of grabbing the host triple (for the current host), we grab (at + // compile time) the target triple that this rustc is built with and + // calling that (at runtime) the host triple. + (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE") +} + +impl Default for Options { + fn default() -> Options { + Options { + crate_types: Vec::new(), + optimize: OptLevel::No, + debuginfo: DebugInfo::None, + lint_opts: Vec::new(), + lint_cap: None, + describe_lints: false, + output_types: OutputTypes(BTreeMap::new()), + search_paths: vec![], + maybe_sysroot: None, + target_triple: TargetTriple::from_triple(host_triple()), + test: false, + incremental: None, + debugging_opts: basic_debugging_options(), + prints: Vec::new(), + borrowck_mode: BorrowckMode::Migrate, + cg: basic_codegen_options(), + error_format: ErrorOutputType::default(), + externs: Externs(BTreeMap::new()), + crate_name: None, + alt_std_name: None, + libs: Vec::new(), + unstable_features: UnstableFeatures::Disallow, + debug_assertions: true, + actually_rustdoc: false, + cli_forced_codegen_units: None, + cli_forced_thinlto_off: false, + remap_path_prefix: Vec::new(), + edition: DEFAULT_EDITION, + json_artifact_notifications: false, + pretty: None, + } + } +} + +impl Options { + /// Returns `true` if there is a reason to build the dep graph. + pub fn build_dep_graph(&self) -> bool { + self.incremental.is_some() || self.debugging_opts.dump_dep_graph + || self.debugging_opts.query_dep_graph + } + + #[inline(always)] + pub fn enable_dep_node_debug_strs(&self) -> bool { + cfg!(debug_assertions) + && (self.debugging_opts.query_dep_graph || self.debugging_opts.incremental_info) + } + + pub fn file_path_mapping(&self) -> FilePathMapping { + FilePathMapping::new(self.remap_path_prefix.clone()) + } + + /// Returns `true` if there will be an output file generated. + pub fn will_create_output_file(&self) -> bool { + !self.debugging_opts.parse_only && // The file is just being parsed + !self.debugging_opts.ls // The file is just being queried + } + + #[inline] + pub fn share_generics(&self) -> bool { + match self.debugging_opts.share_generics { + Some(setting) => setting, + None => { + match self.optimize { + OptLevel::No | + OptLevel::Less | + OptLevel::Size | + OptLevel::SizeMin => true, + OptLevel::Default | + OptLevel::Aggressive => false, + } + } + } + } +} + +// The type of entry function, so users can have their own entry functions +#[derive(Copy, Clone, PartialEq, Hash, Debug)] +pub enum EntryFnType { + Main, + Start, +} + +impl_stable_hash_via_hash!(EntryFnType); + +#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug)] +pub enum CrateType { + Executable, + Dylib, + Rlib, + Staticlib, + Cdylib, + ProcMacro, +} + +impl_stable_hash_via_hash!(CrateType); + +#[derive(Clone, Hash)] +pub enum Passes { + Some(Vec), + All, +} + +impl Passes { + pub fn is_empty(&self) -> bool { + match *self { + Passes::Some(ref v) => v.is_empty(), + Passes::All => false, + } + } +} + +/// Defines all `CodegenOptions`/`DebuggingOptions` fields and parsers all at once. The goal of this +/// macro is to define an interface that can be programmatically used by the option parser +/// to initialize the struct without hardcoding field names all over the place. +/// +/// The goal is to invoke this macro once with the correct fields, and then this macro generates all +/// necessary code. The main gotcha of this macro is the `cgsetters` module which is a bunch of +/// generated code to parse an option into its respective field in the struct. There are a few +/// hand-written parsers for parsing specific types of values in this module. +macro_rules! options { + ($struct_name:ident, $setter_name:ident, $defaultfn:ident, + $buildfn:ident, $prefix:expr, $outputname:expr, + $stat:ident, $mod_desc:ident, $mod_set:ident, + $($opt:ident : $t:ty = ( + $init:expr, + $parse:ident, + [$dep_tracking_marker:ident $(($dep_warn_val:expr, $dep_warn_text:expr))*], + $desc:expr) + ),* ,) => +( + #[derive(Clone)] + pub struct $struct_name { $(pub $opt: $t),* } + + pub fn $defaultfn() -> $struct_name { + $struct_name { $($opt: $init),* } + } + + pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $struct_name + { + let mut op = $defaultfn(); + for option in matches.opt_strs($prefix) { + let mut iter = option.splitn(2, '='); + let key = iter.next().unwrap(); + let value = iter.next(); + let option_to_lookup = key.replace("-", "_"); + let mut found = false; + for &(candidate, setter, opt_type_desc, _) in $stat { + if option_to_lookup != candidate { continue } + if !setter(&mut op, value) { + match (value, opt_type_desc) { + (Some(..), None) => { + early_error(error_format, &format!("{} option `{}` takes no \ + value", $outputname, key)) + } + (None, Some(type_desc)) => { + early_error(error_format, &format!("{0} option `{1}` requires \ + {2} ({3} {1}=)", + $outputname, key, + type_desc, $prefix)) + } + (Some(value), Some(type_desc)) => { + early_error(error_format, &format!("incorrect value `{}` for {} \ + option `{}` - {} was expected", + value, $outputname, + key, type_desc)) + } + (None, None) => panic!() + } + } + found = true; + break; + } + if !found { + early_error(error_format, &format!("unknown {} option: `{}`", + $outputname, key)); + } + } + return op; + } + + impl dep_tracking::DepTrackingHash for $struct_name { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { + let mut sub_hashes = BTreeMap::new(); + $({ + hash_option!($opt, + &self.$opt, + &mut sub_hashes, + [$dep_tracking_marker $($dep_warn_val, + $dep_warn_text, + error_format)*]); + })* + dep_tracking::stable_hash(sub_hashes, hasher, error_format); + } + } + + pub type $setter_name = fn(&mut $struct_name, v: Option<&str>) -> bool; + pub const $stat: &[(&str, $setter_name, Option<&str>, &str)] = + &[ $( (stringify!($opt), $mod_set::$opt, $mod_desc::$parse, $desc) ),* ]; + + #[allow(non_upper_case_globals, dead_code)] + mod $mod_desc { + pub const parse_bool: Option<&str> = None; + pub const parse_opt_bool: Option<&str> = + Some("one of: `y`, `yes`, `on`, `n`, `no`, or `off`"); + pub const parse_string: Option<&str> = Some("a string"); + pub const parse_string_push: Option<&str> = Some("a string"); + pub const parse_pathbuf_push: Option<&str> = Some("a path"); + pub const parse_opt_string: Option<&str> = Some("a string"); + pub const parse_opt_pathbuf: Option<&str> = Some("a path"); + pub const parse_list: Option<&str> = Some("a space-separated list of strings"); + pub const parse_opt_list: Option<&str> = Some("a space-separated list of strings"); + pub const parse_opt_comma_list: Option<&str> = Some("a comma-separated list of strings"); + pub const parse_threads: Option<&str> = Some("a number"); + pub const parse_uint: Option<&str> = Some("a number"); + pub const parse_passes: Option<&str> = + Some("a space-separated list of passes, or `all`"); + pub const parse_opt_uint: Option<&str> = + Some("a number"); + pub const parse_panic_strategy: Option<&str> = + Some("either `unwind` or `abort`"); + pub const parse_relro_level: Option<&str> = + Some("one of: `full`, `partial`, or `off`"); + pub const parse_sanitizer: Option<&str> = + Some("one of: `address`, `leak`, `memory` or `thread`"); + pub const parse_sanitizer_list: Option<&str> = + Some("comma separated list of sanitizers"); + pub const parse_sanitizer_memory_track_origins: Option<&str> = None; + pub const parse_linker_flavor: Option<&str> = + Some(::rustc_target::spec::LinkerFlavor::one_of()); + pub const parse_optimization_fuel: Option<&str> = + Some("crate=integer"); + pub const parse_unpretty: Option<&str> = + Some("`string` or `string=string`"); + pub const parse_treat_err_as_bug: Option<&str> = + Some("either no value or a number bigger than 0"); + pub const parse_lto: Option<&str> = + Some("either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, \ + `fat`, or omitted"); + pub const parse_linker_plugin_lto: Option<&str> = + Some("either a boolean (`yes`, `no`, `on`, `off`, etc), \ + or the path to the linker plugin"); + pub const parse_switch_with_opt_path: Option<&str> = + Some("an optional path to the profiling data output directory"); + pub const parse_merge_functions: Option<&str> = + Some("one of: `disabled`, `trampolines`, or `aliases`"); + pub const parse_symbol_mangling_version: Option<&str> = + Some("either `legacy` or `v0` (RFC 2603)"); + } + + #[allow(dead_code)] + mod $mod_set { + use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath, + SymbolManglingVersion}; + use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; + use std::path::PathBuf; + use std::str::FromStr; + + $( + pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool { + $parse(&mut cg.$opt, v) + } + )* + + fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { + match v { + Some(..) => false, + None => { *slot = true; true } + } + } + + fn parse_opt_bool(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some(s) => { + match s { + "n" | "no" | "off" => { + *slot = Some(false); + } + "y" | "yes" | "on" => { + *slot = Some(true); + } + _ => { return false; } + } + + true + }, + None => { *slot = Some(true); true } + } + } + + fn parse_opt_string(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = Some(s.to_string()); true }, + None => false, + } + } + + fn parse_opt_pathbuf(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = Some(PathBuf::from(s)); true }, + None => false, + } + } + + fn parse_string(slot: &mut String, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = s.to_string(); true }, + None => false, + } + } + + fn parse_string_push(slot: &mut Vec, v: Option<&str>) -> bool { + match v { + Some(s) => { slot.push(s.to_string()); true }, + None => false, + } + } + + fn parse_pathbuf_push(slot: &mut Vec, v: Option<&str>) -> bool { + match v { + Some(s) => { slot.push(PathBuf::from(s)); true }, + None => false, + } + } + + fn parse_list(slot: &mut Vec, v: Option<&str>) + -> bool { + match v { + Some(s) => { + slot.extend(s.split_whitespace().map(|s| s.to_string())); + true + }, + None => false, + } + } + + fn parse_opt_list(slot: &mut Option>, v: Option<&str>) + -> bool { + match v { + Some(s) => { + let v = s.split_whitespace().map(|s| s.to_string()).collect(); + *slot = Some(v); + true + }, + None => false, + } + } + + fn parse_opt_comma_list(slot: &mut Option>, v: Option<&str>) + -> bool { + match v { + Some(s) => { + let v = s.split(',').map(|s| s.to_string()).collect(); + *slot = Some(v); + true + }, + None => false, + } + } + + fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool { + match v.and_then(|s| s.parse().ok()) { + Some(0) => { *slot = ::num_cpus::get(); true }, + Some(i) => { *slot = i; true }, + None => false + } + } + + fn parse_uint(slot: &mut usize, v: Option<&str>) -> bool { + match v.and_then(|s| s.parse().ok()) { + Some(i) => { *slot = i; true }, + None => false + } + } + + fn parse_opt_uint(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = s.parse().ok(); slot.is_some() } + None => { *slot = None; false } + } + } + + fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool { + match v { + Some("all") => { + *slot = Passes::All; + true + } + v => { + let mut passes = vec![]; + if parse_list(&mut passes, v) { + *slot = Passes::Some(passes); + true + } else { + false + } + } + } + } + + fn parse_panic_strategy(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some("unwind") => *slot = Some(PanicStrategy::Unwind), + Some("abort") => *slot = Some(PanicStrategy::Abort), + _ => return false + } + true + } + + fn parse_relro_level(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some(s) => { + match s.parse::() { + Ok(level) => *slot = Some(level), + _ => return false + } + }, + _ => return false + } + true + } + + fn parse_sanitizer(slot: &mut Option, v: Option<&str>) -> bool { + if let Some(Ok(s)) = v.map(str::parse) { + *slot = Some(s); + true + } else { + false + } + } + + fn parse_sanitizer_list(slot: &mut Vec, v: Option<&str>) -> bool { + if let Some(v) = v { + for s in v.split(',').map(str::parse) { + if let Ok(s) = s { + if !slot.contains(&s) { + slot.push(s); + } + } else { + return false; + } + } + true + } else { + false + } + } + + fn parse_sanitizer_memory_track_origins(slot: &mut usize, v: Option<&str>) -> bool { + match v.map(|s| s.parse()) { + None => { + *slot = 2; + true + } + Some(Ok(i)) if i <= 2 => { + *slot = i; + true + } + _ => { + false + } + } + } + + fn parse_linker_flavor(slote: &mut Option, v: Option<&str>) -> bool { + match v.and_then(LinkerFlavor::from_str) { + Some(lf) => *slote = Some(lf), + _ => return false, + } + true + } + + fn parse_optimization_fuel(slot: &mut Option<(String, u64)>, v: Option<&str>) -> bool { + match v { + None => false, + Some(s) => { + let parts = s.split('=').collect::>(); + if parts.len() != 2 { return false; } + let crate_name = parts[0].to_string(); + let fuel = parts[1].parse::(); + if fuel.is_err() { return false; } + *slot = Some((crate_name, fuel.unwrap())); + true + } + } + } + + fn parse_unpretty(slot: &mut Option, v: Option<&str>) -> bool { + match v { + None => false, + Some(s) if s.split('=').count() <= 2 => { + *slot = Some(s.to_string()); + true + } + _ => false, + } + } + + fn parse_treat_err_as_bug(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = s.parse().ok().filter(|&x| x != 0); slot.unwrap_or(0) != 0 } + None => { *slot = Some(1); true } + } + } + + fn parse_lto(slot: &mut LtoCli, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + LtoCli::Yes + } else { + LtoCli::No + }; + return true + } + } + + *slot = match v { + None => LtoCli::NoParam, + Some("thin") => LtoCli::Thin, + Some("fat") => LtoCli::Fat, + Some(_) => return false, + }; + true + } + + fn parse_linker_plugin_lto(slot: &mut LinkerPluginLto, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + LinkerPluginLto::LinkerPluginAuto + } else { + LinkerPluginLto::Disabled + }; + return true + } + } + + *slot = match v { + None => LinkerPluginLto::LinkerPluginAuto, + Some(path) => LinkerPluginLto::LinkerPlugin(PathBuf::from(path)), + }; + true + } + + fn parse_switch_with_opt_path(slot: &mut SwitchWithOptPath, v: Option<&str>) -> bool { + *slot = match v { + None => SwitchWithOptPath::Enabled(None), + Some(path) => SwitchWithOptPath::Enabled(Some(PathBuf::from(path))), + }; + true + } + + fn parse_merge_functions(slot: &mut Option, v: Option<&str>) -> bool { + match v.and_then(|s| MergeFunctions::from_str(s).ok()) { + Some(mergefunc) => *slot = Some(mergefunc), + _ => return false, + } + true + } + + fn parse_symbol_mangling_version( + slot: &mut SymbolManglingVersion, + v: Option<&str>, + ) -> bool { + *slot = match v { + Some("legacy") => SymbolManglingVersion::Legacy, + Some("v0") => SymbolManglingVersion::V0, + _ => return false, + }; + true + } + } +) } + +options! {CodegenOptions, CodegenSetter, basic_codegen_options, + build_codegen_options, "C", "codegen", + CG_OPTIONS, cg_type_desc, cgsetters, + ar: Option = (None, parse_opt_string, [UNTRACKED], + "this option is deprecated and does nothing"), + linker: Option = (None, parse_opt_pathbuf, [UNTRACKED], + "system linker to link outputs with"), + link_arg: Vec = (vec![], parse_string_push, [UNTRACKED], + "a single extra argument to append to the linker invocation (can be used several times)"), + link_args: Option> = (None, parse_opt_list, [UNTRACKED], + "extra arguments to append to the linker invocation (space separated)"), + link_dead_code: bool = (false, parse_bool, [UNTRACKED], + "don't let linker strip dead code (turning it on can be used for code coverage)"), + lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED], + "perform LLVM link-time optimizations"), + target_cpu: Option = (None, parse_opt_string, [TRACKED], + "select target processor (`rustc --print target-cpus` for details)"), + target_feature: String = (String::new(), parse_string, [TRACKED], + "target specific attributes. (`rustc --print target-features` for details). \ + This feature is unsafe."), + passes: Vec = (Vec::new(), parse_list, [TRACKED], + "a list of extra LLVM passes to run (space separated)"), + llvm_args: Vec = (Vec::new(), parse_list, [TRACKED], + "a list of arguments to pass to LLVM (space separated)"), + save_temps: bool = (false, parse_bool, [UNTRACKED], + "save all temporary output files during compilation"), + rpath: bool = (false, parse_bool, [UNTRACKED], + "set rpath values in libs/exes"), + overflow_checks: Option = (None, parse_opt_bool, [TRACKED], + "use overflow checks for integer arithmetic"), + no_prepopulate_passes: bool = (false, parse_bool, [TRACKED], + "don't pre-populate the pass manager with a list of passes"), + no_vectorize_loops: bool = (false, parse_bool, [TRACKED], + "don't run the loop vectorization optimization passes"), + no_vectorize_slp: bool = (false, parse_bool, [TRACKED], + "don't run LLVM's SLP vectorization pass"), + soft_float: bool = (false, parse_bool, [TRACKED], + "use soft float ABI (*eabihf targets only)"), + prefer_dynamic: bool = (false, parse_bool, [TRACKED], + "prefer dynamic linking to static linking"), + no_integrated_as: bool = (false, parse_bool, [TRACKED], + "use an external assembler rather than LLVM's integrated one"), + no_redzone: Option = (None, parse_opt_bool, [TRACKED], + "disable the use of the redzone"), + relocation_model: Option = (None, parse_opt_string, [TRACKED], + "choose the relocation model to use (`rustc --print relocation-models` for details)"), + code_model: Option = (None, parse_opt_string, [TRACKED], + "choose the code model to use (`rustc --print code-models` for details)"), + metadata: Vec = (Vec::new(), parse_list, [TRACKED], + "metadata to mangle symbol names with"), + extra_filename: String = (String::new(), parse_string, [UNTRACKED], + "extra data to put in each output filename"), + codegen_units: Option = (None, parse_opt_uint, [UNTRACKED], + "divide crate into N units to optimize in parallel"), + remark: Passes = (Passes::Some(Vec::new()), parse_passes, [UNTRACKED], + "print remarks for these optimization passes (space separated, or \"all\")"), + no_stack_check: bool = (false, parse_bool, [UNTRACKED], + "the `--no-stack-check` flag is deprecated and does nothing"), + debuginfo: Option = (None, parse_opt_uint, [TRACKED], + "debug info emission level, 0 = no debug info, 1 = line tables only, \ + 2 = full debug info with variable and type information"), + opt_level: Option = (None, parse_opt_string, [TRACKED], + "optimize with possible levels 0-3, s, or z"), + force_frame_pointers: Option = (None, parse_opt_bool, [TRACKED], + "force use of the frame pointers"), + debug_assertions: Option = (None, parse_opt_bool, [TRACKED], + "explicitly enable the `cfg(debug_assertions)` directive"), + inline_threshold: Option = (None, parse_opt_uint, [TRACKED], + "set the threshold for inlining a function (default: 225)"), + panic: Option = (None, parse_panic_strategy, + [TRACKED], "panic strategy to compile crate with"), + incremental: Option = (None, parse_opt_string, [UNTRACKED], + "enable incremental compilation"), + default_linker_libraries: Option = (None, parse_opt_bool, [UNTRACKED], + "allow the linker to link its default libraries"), + linker_flavor: Option = (None, parse_linker_flavor, [UNTRACKED], + "linker flavor"), + linker_plugin_lto: LinkerPluginLto = (LinkerPluginLto::Disabled, + parse_linker_plugin_lto, [TRACKED], + "generate build artifacts that are compatible with linker-based LTO."), + profile_generate: SwitchWithOptPath = (SwitchWithOptPath::Disabled, + parse_switch_with_opt_path, [TRACKED], + "compile the program with profiling instrumentation"), + profile_use: Option = (None, parse_opt_pathbuf, [TRACKED], + "use the given `.profdata` file for profile-guided optimization"), +} + +options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, + build_debugging_options, "Z", "debugging", + DB_OPTIONS, db_type_desc, dbsetters, + codegen_backend: Option = (None, parse_opt_string, [TRACKED], + "the backend to use"), + verbose: bool = (false, parse_bool, [UNTRACKED], + "in general, enable more debug printouts"), + span_free_formats: bool = (false, parse_bool, [UNTRACKED], + "when debug-printing compiler state, do not include spans"), // o/w tests have closure@path + identify_regions: bool = (false, parse_bool, [UNTRACKED], + "make unnamed regions display as '# (where # is some non-ident unique id)"), + borrowck: Option = (None, parse_opt_string, [UNTRACKED], + "select which borrowck is used (`mir` or `migrate`)"), + time_passes: bool = (false, parse_bool, [UNTRACKED], + "measure time of each rustc pass"), + time: bool = (false, parse_bool, [UNTRACKED], + "measure time of rustc processes"), + time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], + "measure time of each LLVM pass"), + input_stats: bool = (false, parse_bool, [UNTRACKED], + "gather statistics about the input"), + asm_comments: bool = (false, parse_bool, [TRACKED], + "generate comments into the assembly (may change behavior)"), + verify_llvm_ir: bool = (false, parse_bool, [TRACKED], + "verify LLVM IR"), + borrowck_stats: bool = (false, parse_bool, [UNTRACKED], + "gather borrowck statistics"), + no_landing_pads: bool = (false, parse_bool, [TRACKED], + "omit landing pads for unwinding"), + fewer_names: bool = (false, parse_bool, [TRACKED], + "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR)"), + meta_stats: bool = (false, parse_bool, [UNTRACKED], + "gather metadata statistics"), + print_link_args: bool = (false, parse_bool, [UNTRACKED], + "print the arguments passed to the linker"), + print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], + "prints the LLVM optimization passes being run"), + ast_json: bool = (false, parse_bool, [UNTRACKED], + "print the AST as JSON and halt"), + // We default to 1 here since we want to behave like + // a sequential compiler for now. This'll likely be adjusted + // in the future. Note that -Zthreads=0 is the way to get + // the num_cpus behavior. + threads: usize = (1, parse_threads, [UNTRACKED], + "use a thread pool with N threads"), + ast_json_noexpand: bool = (false, parse_bool, [UNTRACKED], + "print the pre-expansion AST as JSON and halt"), + ls: bool = (false, parse_bool, [UNTRACKED], + "list the symbols defined by a library crate"), + save_analysis: bool = (false, parse_bool, [UNTRACKED], + "write syntax and type analysis (in JSON format) information, in \ + addition to normal output"), + print_region_graph: bool = (false, parse_bool, [UNTRACKED], + "prints region inference graph. \ + Use with RUST_REGION_GRAPH=help for more info"), + parse_only: bool = (false, parse_bool, [UNTRACKED], + "parse only; do not compile, assemble, or link"), + dual_proc_macros: bool = (false, parse_bool, [TRACKED], + "load proc macros for both target and host, but only link to the target"), + no_codegen: bool = (false, parse_bool, [TRACKED], + "run all passes except codegen; no output"), + treat_err_as_bug: Option = (None, parse_treat_err_as_bug, [TRACKED], + "treat error number `val` that occurs as bug"), + report_delayed_bugs: bool = (false, parse_bool, [TRACKED], + "immediately print bugs registered with `delay_span_bug`"), + external_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], + "show macro backtraces even for non-local macros"), + teach: bool = (false, parse_bool, [TRACKED], + "show extended diagnostic help"), + terminal_width: Option = (None, parse_opt_uint, [UNTRACKED], + "set the current terminal width"), + panic_abort_tests: bool = (false, parse_bool, [TRACKED], + "support compiling tests with panic=abort"), + continue_parse_after_error: bool = (false, parse_bool, [TRACKED], + "attempt to recover from parse errors (experimental)"), + dep_tasks: bool = (false, parse_bool, [UNTRACKED], + "print tasks that execute and the color their dep node gets (requires debug build)"), + incremental: Option = (None, parse_opt_string, [UNTRACKED], + "enable incremental compilation (experimental)"), + incremental_queries: bool = (true, parse_bool, [UNTRACKED], + "enable incremental compilation support for queries (experimental)"), + incremental_info: bool = (false, parse_bool, [UNTRACKED], + "print high-level information about incremental reuse (or the lack thereof)"), + incremental_dump_hash: bool = (false, parse_bool, [UNTRACKED], + "dump hash information in textual format to stdout"), + incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], + "verify incr. comp. hashes of green query instances"), + incremental_ignore_spans: bool = (false, parse_bool, [UNTRACKED], + "ignore spans during ICH computation -- used for testing"), + instrument_mcount: bool = (false, parse_bool, [TRACKED], + "insert function instrument code for mcount-based tracing"), + dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], + "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"), + query_dep_graph: bool = (false, parse_bool, [UNTRACKED], + "enable queries of the dependency graph for regression testing"), + no_analysis: bool = (false, parse_bool, [UNTRACKED], + "parse and expand the source, but run no analysis"), + unstable_options: bool = (false, parse_bool, [UNTRACKED], + "adds unstable command line options to rustc interface"), + force_overflow_checks: Option = (None, parse_opt_bool, [TRACKED], + "force overflow checks on or off"), + trace_macros: bool = (false, parse_bool, [UNTRACKED], + "for every macro invocation, print its name and arguments"), + debug_macros: bool = (false, parse_bool, [TRACKED], + "emit line numbers debug info inside macros"), + generate_arange_section: bool = (true, parse_bool, [TRACKED], + "generate DWARF address ranges for faster lookups"), + keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], + "don't clear the hygiene data after analysis"), + keep_ast: bool = (false, parse_bool, [UNTRACKED], + "keep the AST after lowering it to HIR"), + show_span: Option = (None, parse_opt_string, [TRACKED], + "show spans for compiler debugging (expr|pat|ty)"), + print_type_sizes: bool = (false, parse_bool, [UNTRACKED], + "print layout information for each type encountered"), + print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], + "print the result of the monomorphization collection pass"), + mir_opt_level: usize = (1, parse_uint, [TRACKED], + "set the MIR optimization level (0-3, default: 1)"), + mutable_noalias: Option = (None, parse_opt_bool, [TRACKED], + "emit noalias metadata for mutable references (default: no)"), + dump_mir: Option = (None, parse_opt_string, [UNTRACKED], + "dump MIR state to file. + `val` is used to select which passes and functions to dump. For example: + `all` matches all passes and functions, + `foo` matches all passes for functions whose name contains 'foo', + `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo', + `foo | bar` all passes for function names containing 'foo' or 'bar'."), + + dump_mir_dir: String = (String::from("mir_dump"), parse_string, [UNTRACKED], + "the directory the MIR is dumped into"), + dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], + "in addition to `.mir` files, create graphviz `.dot` files"), + dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED], + "if set, exclude the pass number when dumping MIR (used in tests)"), + mir_emit_retag: bool = (false, parse_bool, [TRACKED], + "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0"), + perf_stats: bool = (false, parse_bool, [UNTRACKED], + "print some performance-related statistics"), + query_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about the query system"), + hir_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about AST and HIR"), + always_encode_mir: bool = (false, parse_bool, [TRACKED], + "encode MIR of all functions into the crate metadata"), + json_rendered: Option = (None, parse_opt_string, [UNTRACKED], + "describes how to render the `rendered` field of json diagnostics"), + unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], + "take the breaks off const evaluation. NOTE: this is unsound"), + osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], + "pass `-install_name @rpath/...` to the macOS linker"), + sanitizer: Option = (None, parse_sanitizer, [TRACKED], + "use a sanitizer"), + sanitizer_recover: Vec = (vec![], parse_sanitizer_list, [TRACKED], + "Enable recovery for selected sanitizers"), + sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], + "Enable origins tracking in MemorySanitizer"), + fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], + "set the optimization fuel quota for a crate"), + print_fuel: Option = (None, parse_opt_string, [TRACKED], + "make rustc print the total optimization fuel used by a crate"), + force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], + "force all crates to be `rustc_private` unstable"), + pre_link_arg: Vec = (vec![], parse_string_push, [UNTRACKED], + "a single extra argument to prepend the linker invocation (can be used several times)"), + pre_link_args: Option> = (None, parse_opt_list, [UNTRACKED], + "extra arguments to prepend to the linker invocation (space separated)"), + profile: bool = (false, parse_bool, [TRACKED], + "insert profiling code"), + disable_instrumentation_preinliner: bool = (false, parse_bool, [TRACKED], + "Disable the instrumentation pre-inliner, useful for profiling / PGO."), + relro_level: Option = (None, parse_relro_level, [TRACKED], + "choose which RELRO level to use"), + nll_facts: bool = (false, parse_bool, [UNTRACKED], + "dump facts from NLL analysis into side files"), + nll_dont_emit_read_for_match: bool = (false, parse_bool, [UNTRACKED], + "in match codegen, do not include FakeRead statements (used by mir-borrowck)"), + dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], + "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting)."), + polonius: bool = (false, parse_bool, [UNTRACKED], + "enable polonius-based borrow-checker"), + codegen_time_graph: bool = (false, parse_bool, [UNTRACKED], + "generate a graphical HTML report of time spent in codegen and LLVM"), + thinlto: Option = (None, parse_opt_bool, [TRACKED], + "enable ThinLTO when possible"), + inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], + "control whether `#[inline]` functions are in all CGUs"), + tls_model: Option = (None, parse_opt_string, [TRACKED], + "choose the TLS model to use (`rustc --print tls-models` for details)"), + saturating_float_casts: bool = (false, parse_bool, [TRACKED], + "make float->int casts UB-free: numbers outside the integer type's range are clipped to \ + the max/min integer respectively, and NaN is mapped to 0"), + human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], + "generate human-readable, predictable names for codegen units"), + dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], + "in dep-info output, omit targets for tracking dependencies of the dep-info files \ + themselves"), + unpretty: Option = (None, parse_unpretty, [UNTRACKED], + "present the input source, unstable (and less-pretty) variants; + valid types are any of the types for `--pretty`, as well as: + `expanded`, `expanded,identified`, + `expanded,hygiene` (with internal representations), + `everybody_loops` (all function bodies replaced with `loop {}`), + `hir` (the HIR), `hir,identified`, + `hir,typed` (HIR with types for each node), + `hir-tree` (dump the raw HIR), + `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR)"), + run_dsymutil: Option = (None, parse_opt_bool, [TRACKED], + "run `dsymutil` and delete intermediate object files"), + ui_testing: bool = (false, parse_bool, [UNTRACKED], + "format compiler diagnostics in a way that's better suitable for UI testing"), + embed_bitcode: bool = (false, parse_bool, [TRACKED], + "embed LLVM bitcode in object files"), + strip_debuginfo_if_disabled: Option = (None, parse_opt_bool, [TRACKED], + "tell the linker to strip debuginfo when building without debuginfo enabled."), + share_generics: Option = (None, parse_opt_bool, [TRACKED], + "make the current crate share its generic instantiations"), + chalk: bool = (false, parse_bool, [TRACKED], + "enable the experimental Chalk-based trait solving engine"), + no_parallel_llvm: bool = (false, parse_bool, [UNTRACKED], + "don't run LLVM in parallel (while keeping codegen-units and ThinLTO)"), + no_leak_check: bool = (false, parse_bool, [UNTRACKED], + "disables the 'leak check' for subtyping; unsound, but useful for tests"), + no_interleave_lints: bool = (false, parse_bool, [UNTRACKED], + "don't interleave execution of lints; allows benchmarking individual lints"), + crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], + "inject the given attribute in the crate"), + self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled, + parse_switch_with_opt_path, [UNTRACKED], + "run the self profiler and output the raw event data"), + self_profile_events: Option> = (None, parse_opt_comma_list, [UNTRACKED], + "specifies which kinds of events get recorded by the self profiler"), + emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], + "emits a section containing stack size metadata"), + plt: Option = (None, parse_opt_bool, [TRACKED], + "whether to use the PLT when calling into shared libraries; + only has effect for PIC code on systems with ELF binaries + (default: PLT is disabled if full relro is enabled)"), + merge_functions: Option = (None, parse_merge_functions, [TRACKED], + "control the operation of the MergeFunctions LLVM pass, taking + the same values as the target option of the same name"), + allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], + "only allow the listed language features to be enabled in code (space separated)"), + symbol_mangling_version: SymbolManglingVersion = (SymbolManglingVersion::Legacy, + parse_symbol_mangling_version, [TRACKED], + "which mangling version to use for symbol names"), + binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], + "include artifacts (sysroot, crate dependencies) used during compilation in dep-info"), + insert_sideeffect: bool = (false, parse_bool, [TRACKED], + "fix undefined behavior when a thread doesn't eventually make progress \ + (such as entering an empty infinite loop) by inserting llvm.sideeffect"), +} + +pub const fn default_lib_output() -> CrateType { + CrateType::Rlib +} + +pub fn default_configuration(sess: &Session) -> CrateConfig { + let end = &sess.target.target.target_endian; + let arch = &sess.target.target.arch; + let wordsz = &sess.target.target.target_pointer_width; + let os = &sess.target.target.target_os; + let env = &sess.target.target.target_env; + let vendor = &sess.target.target.target_vendor; + let min_atomic_width = sess.target.target.min_atomic_width(); + let max_atomic_width = sess.target.target.max_atomic_width(); + let atomic_cas = sess.target.target.options.atomic_cas; + + let mut ret = FxHashSet::default(); + ret.reserve(6); // the minimum number of insertions + // Target bindings. + ret.insert((Symbol::intern("target_os"), Some(Symbol::intern(os)))); + if let Some(ref fam) = sess.target.target.options.target_family { + ret.insert((Symbol::intern("target_family"), Some(Symbol::intern(fam)))); + if fam == "windows" || fam == "unix" { + ret.insert((Symbol::intern(fam), None)); + } + } + ret.insert((Symbol::intern("target_arch"), Some(Symbol::intern(arch)))); + ret.insert((Symbol::intern("target_endian"), Some(Symbol::intern(end)))); + ret.insert(( + Symbol::intern("target_pointer_width"), + Some(Symbol::intern(wordsz)), + )); + ret.insert((Symbol::intern("target_env"), Some(Symbol::intern(env)))); + ret.insert(( + Symbol::intern("target_vendor"), + Some(Symbol::intern(vendor)), + )); + if sess.target.target.options.has_elf_tls { + ret.insert((sym::target_thread_local, None)); + } + for &i in &[8, 16, 32, 64, 128] { + if i >= min_atomic_width && i <= max_atomic_width { + let mut insert_atomic = |s| { + ret.insert(( + sym::target_has_atomic_load_store, + Some(Symbol::intern(s)), + )); + if atomic_cas { + ret.insert(( + sym::target_has_atomic, + Some(Symbol::intern(s)) + )); + } + }; + let s = i.to_string(); + insert_atomic(&s); + if &s == wordsz { + insert_atomic("ptr"); + } + } + } + if let Some(s) = &sess.opts.debugging_opts.sanitizer { + let symbol = Symbol::intern(&s.to_string()); + ret.insert((sym::sanitize, Some(symbol))); + } + if sess.opts.debug_assertions { + ret.insert((Symbol::intern("debug_assertions"), None)); + } + if sess.opts.crate_types.contains(&CrateType::ProcMacro) { + ret.insert((sym::proc_macro, None)); + } + ret +} + +/// Converts the crate `cfg!` configuration from `String` to `Symbol`. +/// `rustc_interface::interface::Config` accepts this in the compiler configuration, +/// but the symbol interner is not yet set up then, so we must convert it later. +pub fn to_crate_config(cfg: FxHashSet<(String, Option)>) -> CrateConfig { + cfg.into_iter() + .map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))) + .collect() +} + +pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig { + // Combine the configuration requested by the session (command line) with + // some default and generated configuration items. + let default_cfg = default_configuration(sess); + // If the user wants a test runner, then add the test cfg. + if sess.opts.test { + user_cfg.insert((sym::test, None)); + } + user_cfg.extend(default_cfg.iter().cloned()); + user_cfg +} + +pub fn build_target_config(opts: &Options, sp: &Handler) -> Config { + let target = Target::search(&opts.target_triple).unwrap_or_else(|e| { + sp.struct_fatal(&format!("Error loading target specification: {}", e)) + .help("Use `--print target-list` for a list of built-in targets") + .emit(); + FatalError.raise(); + }); + + let ptr_width = match &target.target_pointer_width[..] { + "16" => 16, + "32" => 32, + "64" => 64, + w => sp.fatal(&format!( + "target specification was invalid: \ + unrecognized target-pointer-width {}", + w + )).raise(), + }; + + Config { + target, + ptr_width, + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum OptionStability { + Stable, + Unstable, +} + +pub struct RustcOptGroup { + pub apply: Box &mut getopts::Options>, + pub name: &'static str, + pub stability: OptionStability, +} + +impl RustcOptGroup { + pub fn is_stable(&self) -> bool { + self.stability == OptionStability::Stable + } + + pub fn stable(name: &'static str, f: F) -> RustcOptGroup + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup { + name, + apply: Box::new(f), + stability: OptionStability::Stable, + } + } + + pub fn unstable(name: &'static str, f: F) -> RustcOptGroup + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup { + name, + apply: Box::new(f), + stability: OptionStability::Unstable, + } + } +} + +// The `opt` local module holds wrappers around the `getopts` API that +// adds extra rustc-specific metadata to each option; such metadata +// is exposed by . The public +// functions below ending with `_u` are the functions that return +// *unstable* options, i.e., options that are only enabled when the +// user also passes the `-Z unstable-options` debugging flag. +mod opt { + // The `fn flag*` etc below are written so that we can use them + // in the future; do not warn about them not being used right now. + #![allow(dead_code)] + + use getopts; + use super::RustcOptGroup; + + pub type R = RustcOptGroup; + pub type S = &'static str; + + fn stable(name: S, f: F) -> R + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup::stable(name, f) + } + + fn unstable(name: S, f: F) -> R + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup::unstable(name, f) + } + + fn longer(a: S, b: S) -> S { + if a.len() > b.len() { + a + } else { + b + } + } + + pub fn opt_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) + } + pub fn multi_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) + } + pub fn flag_s(a: S, b: S, c: S) -> R { + stable(longer(a, b), move |opts| opts.optflag(a, b, c)) + } + pub fn flagopt_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) + } + pub fn flagmulti_s(a: S, b: S, c: S) -> R { + stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) + } + + pub fn opt(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) + } + pub fn multi(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) + } + pub fn flag(a: S, b: S, c: S) -> R { + unstable(longer(a, b), move |opts| opts.optflag(a, b, c)) + } + pub fn flagopt(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) + } + pub fn flagmulti(a: S, b: S, c: S) -> R { + unstable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) + } +} + +/// Returns the "short" subset of the rustc command line options, +/// including metadata for each option, such as whether the option is +/// part of the stable long-term interface for rustc. +pub fn rustc_short_optgroups() -> Vec { + vec![ + opt::flag_s("h", "help", "Display this message"), + opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"), + opt::multi_s( + "L", + "", + "Add a directory to the library search path. The + optional KIND can be one of dependency, crate, native, + framework, or all (the default).", + "[KIND=]PATH", + ), + opt::multi_s( + "l", + "", + "Link the generated crate(s) to the specified native + library NAME. The optional KIND can be one of + static, framework, or dylib (the default).", + "[KIND=]NAME", + ), + make_crate_type_option(), + opt::opt_s( + "", + "crate-name", + "Specify the name of the crate being built", + "NAME", + ), + opt::opt_s( + "", + "edition", + "Specify which edition of the compiler to use when compiling code.", + EDITION_NAME_LIST, + ), + opt::multi_s( + "", + "emit", + "Comma separated list of types of output for \ + the compiler to emit", + "[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]", + ), + opt::multi_s( + "", + "print", + "Compiler information to print on stdout", + "[crate-name|file-names|sysroot|cfg|target-list|\ + target-cpus|target-features|relocation-models|\ + code-models|tls-models|target-spec-json|native-static-libs]", + ), + opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), + opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), + opt::opt_s("o", "", "Write output to ", "FILENAME"), + opt::opt_s( + "", + "out-dir", + "Write output to compiler-chosen filename \ + in ", + "DIR", + ), + opt::opt_s( + "", + "explain", + "Provide a detailed explanation of an error \ + message", + "OPT", + ), + opt::flag_s("", "test", "Build a test harness"), + opt::opt_s( + "", + "target", + "Target triple for which the code is compiled", + "TARGET", + ), + opt::multi_s("W", "warn", "Set lint warnings", "OPT"), + opt::multi_s("A", "allow", "Set lint allowed", "OPT"), + opt::multi_s("D", "deny", "Set lint denied", "OPT"), + opt::multi_s("F", "forbid", "Set lint forbidden", "OPT"), + opt::multi_s( + "", + "cap-lints", + "Set the most restrictive lint level. \ + More restrictive lints are capped at this \ + level", + "LEVEL", + ), + opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), + opt::flag_s("V", "version", "Print version info and exit"), + opt::flag_s("v", "verbose", "Use verbose output"), + ] +} + +/// Returns all rustc command line options, including metadata for +/// each option, such as whether the option is part of the stable +/// long-term interface for rustc. +pub fn rustc_optgroups() -> Vec { + let mut opts = rustc_short_optgroups(); + opts.extend(vec![ + opt::multi_s( + "", + "extern", + "Specify where an external rust library is located", + "NAME[=PATH]", + ), + opt::multi_s( + "", + "extern-private", + "Specify where an extern rust library is located, marking it as a private dependency", + "NAME=PATH", + ), + opt::opt_s("", "sysroot", "Override the system root", "PATH"), + opt::multi("Z", "", "Set internal debugging options", "FLAG"), + opt::opt_s( + "", + "error-format", + "How errors and other messages are produced", + "human|json|short", + ), + opt::multi_s( + "", + "json", + "Configure the JSON output of the compiler", + "CONFIG", + ), + opt::opt_s( + "", + "color", + "Configure coloring of output: + auto = colorize, if output goes to a tty (default); + always = always colorize output; + never = never colorize output", + "auto|always|never", + ), + opt::opt( + "", + "pretty", + "Pretty-print the input instead of compiling; + valid types are: `normal` (un-annotated source), + `expanded` (crates expanded), or + `expanded,identified` (fully parenthesized, AST nodes with IDs).", + "TYPE", + ), + opt::multi_s( + "", + "remap-path-prefix", + "Remap source names in all output (compiler messages and output files)", + "FROM=TO", + ), + ]); + opts +} + +pub fn get_cmd_lint_options(matches: &getopts::Matches, + error_format: ErrorOutputType) + -> (Vec<(String, lint::Level)>, bool, Option) { + let mut lint_opts = vec![]; + let mut describe_lints = false; + + for &level in &[lint::Allow, lint::Warn, lint::Deny, lint::Forbid] { + for lint_name in matches.opt_strs(level.as_str()) { + if lint_name == "help" { + describe_lints = true; + } else { + lint_opts.push((lint_name.replace("-", "_"), level)); + } + } + } + + let lint_cap = matches.opt_str("cap-lints").map(|cap| { + lint::Level::from_str(&cap) + .unwrap_or_else(|| early_error(error_format, &format!("unknown lint level: `{}`", cap))) + }); + (lint_opts, describe_lints, lint_cap) +} + +/// Parses the `--color` flag. +pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { + match matches.opt_str("color").as_ref().map(|s| &s[..]) { + Some("auto") => ColorConfig::Auto, + Some("always") => ColorConfig::Always, + Some("never") => ColorConfig::Never, + + None => ColorConfig::Auto, + + Some(arg) => early_error( + ErrorOutputType::default(), + &format!( + "argument for `--color` must be auto, \ + always or never (instead was `{}`)", + arg + ), + ), + } +} + +/// Parse the `--json` flag. +/// +/// The first value returned is how to render JSON diagnostics, and the second +/// is whether or not artifact notifications are enabled. +pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) { + let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = + HumanReadableErrorType::Default; + let mut json_color = ColorConfig::Never; + let mut json_artifact_notifications = false; + for option in matches.opt_strs("json") { + // For now conservatively forbid `--color` with `--json` since `--json` + // won't actually be emitting any colors and anything colorized is + // embedded in a diagnostic message anyway. + if matches.opt_str("color").is_some() { + early_error( + ErrorOutputType::default(), + "cannot specify the `--color` option with `--json`", + ); + } + + for sub_option in option.split(',') { + match sub_option { + "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, + "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, + "artifacts" => json_artifact_notifications = true, + s => { + early_error( + ErrorOutputType::default(), + &format!("unknown `--json` option `{}`", s), + ) + } + } + } + } + (json_rendered(json_color), json_artifact_notifications) +} + +/// Parses the `--error-format` flag. +pub fn parse_error_format( + matches: &getopts::Matches, + color: ColorConfig, + json_rendered: HumanReadableErrorType, +) -> ErrorOutputType { + // We need the `opts_present` check because the driver will send us Matches + // with only stable options if no unstable options are used. Since error-format + // is unstable, it will not be present. We have to use `opts_present` not + // `opt_present` because the latter will panic. + let error_format = if matches.opts_present(&["error-format".to_owned()]) { + match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { + None | + Some("human") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), + Some("human-annotate-rs") => { + ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color)) + }, + Some("json") => ErrorOutputType::Json { pretty: false, json_rendered }, + Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, + Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), + + Some(arg) => early_error( + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), + &format!( + "argument for `--error-format` must be `human`, `json` or \ + `short` (instead was `{}`)", + arg + ), + ), + } + } else { + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + }; + + match error_format { + ErrorOutputType::Json { .. } => {} + + // Conservatively require that the `--json` argument is coupled with + // `--error-format=json`. This means that `--json` is specified we + // should actually be emitting JSON blobs. + _ if matches.opt_strs("json").len() > 0 => { + early_error( + ErrorOutputType::default(), + "using `--json` requires also using `--error-format=json`", + ); + } + + _ => {} + } + + return error_format; +} + +fn parse_crate_edition(matches: &getopts::Matches) -> Edition { + let edition = match matches.opt_str("edition") { + Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| + early_error( + ErrorOutputType::default(), + &format!( + "argument for `--edition` must be one of: \ + {}. (instead was `{}`)", + EDITION_NAME_LIST, + arg + ), + ), + ), + None => DEFAULT_EDITION, + }; + + if !edition.is_stable() && !nightly_options::is_nightly_build() { + early_error( + ErrorOutputType::default(), + &format!( + "edition {} is unstable and only \ + available for nightly builds of rustc.", + edition, + ) + ) + } + + edition +} + +fn check_debug_option_stability( + debugging_opts: &DebuggingOptions, + error_format: ErrorOutputType, + json_rendered: HumanReadableErrorType, +) { + if !debugging_opts.unstable_options { + if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { + early_error( + ErrorOutputType::Json { pretty: false, json_rendered }, + "`--error-format=pretty-json` is unstable", + ); + } + if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = + error_format { + early_error( + ErrorOutputType::Json { pretty: false, json_rendered }, + "`--error-format=human-annotate-rs` is unstable", + ); + } + } +} + +fn parse_output_types( + debugging_opts: &DebuggingOptions, + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> OutputTypes { + let mut output_types = BTreeMap::new(); + if !debugging_opts.parse_only { + for list in matches.opt_strs("emit") { + for output_type in list.split(',') { + let mut parts = output_type.splitn(2, '='); + let shorthand = parts.next().unwrap(); + let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| + early_error( + error_format, + &format!( + "unknown emission type: `{}` - expected one of: {}", + shorthand, + OutputType::shorthands_display(), + ), + ), + ); + let path = parts.next().map(PathBuf::from); + output_types.insert(output_type, path); + } + } + }; + if output_types.is_empty() { + output_types.insert(OutputType::Exe, None); + } + OutputTypes(output_types) +} + +fn should_override_cgus_and_disable_thinlto( + output_types: &OutputTypes, + matches: &getopts::Matches, + error_format: ErrorOutputType, + mut codegen_units: Option, +) -> (bool, Option) { + let mut disable_thinlto = false; + // Issue #30063: if user requests LLVM-related output to one + // particular path, disable codegen-units. + let incompatible: Vec<_> = output_types.0 + .iter() + .map(|ot_path| ot_path.0) + .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file()) + .map(|ot| ot.shorthand()) + .collect(); + if !incompatible.is_empty() { + match codegen_units { + Some(n) if n > 1 => { + if matches.opt_present("o") { + for ot in &incompatible { + early_warn( + error_format, + &format!( + "`--emit={}` with `-o` incompatible with \ + `-C codegen-units=N` for N > 1", + ot + ), + ); + } + early_warn(error_format, "resetting to default -C codegen-units=1"); + codegen_units = Some(1); + disable_thinlto = true; + } + } + _ => { + codegen_units = Some(1); + disable_thinlto = true; + } + } + } + + if codegen_units == Some(0) { + early_error( + error_format, + "value for codegen units must be a positive non-zero integer", + ); + } + + (disable_thinlto, codegen_units) +} + +fn check_thread_count(debugging_opts: &DebuggingOptions, error_format: ErrorOutputType) { + if debugging_opts.threads == 0 { + early_error( + error_format, + "value for threads must be a positive non-zero integer", + ); + } + + if debugging_opts.threads > 1 && debugging_opts.fuel.is_some() { + early_error( + error_format, + "optimization fuel is incompatible with multiple threads", + ); + } +} + +fn select_incremental_path( + debugging_opts: &DebuggingOptions, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> Option { + match (&debugging_opts.incremental, &cg.incremental) { + (Some(path1), Some(path2)) => { + if path1 != path2 { + early_error( + error_format, + &format!( + "conflicting paths for `-Z incremental` and \ + `-C incremental` specified: {} versus {}", + path1, path2 + ), + ); + } else { + Some(path1) + } + } + (Some(path), None) => Some(path), + (None, Some(path)) => Some(path), + (None, None) => None, + }.map(|m| PathBuf::from(m)) +} + +fn collect_print_requests( + cg: &mut CodegenOptions, + dopts: &mut DebuggingOptions, + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> Vec { + let mut prints = Vec::::new(); + if cg.target_cpu.as_ref().map_or(false, |s| s == "help") { + prints.push(PrintRequest::TargetCPUs); + cg.target_cpu = None; + }; + if cg.target_feature == "help" { + prints.push(PrintRequest::TargetFeatures); + cg.target_feature = String::new(); + } + if cg.relocation_model.as_ref().map_or(false, |s| s == "help") { + prints.push(PrintRequest::RelocationModels); + cg.relocation_model = None; + } + if cg.code_model.as_ref().map_or(false, |s| s == "help") { + prints.push(PrintRequest::CodeModels); + cg.code_model = None; + } + if dopts + .tls_model + .as_ref() + .map_or(false, |s| s == "help") + { + prints.push(PrintRequest::TlsModels); + dopts.tls_model = None; + } + + prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s { + "crate-name" => PrintRequest::CrateName, + "file-names" => PrintRequest::FileNames, + "sysroot" => PrintRequest::Sysroot, + "cfg" => PrintRequest::Cfg, + "target-list" => PrintRequest::TargetList, + "target-cpus" => PrintRequest::TargetCPUs, + "target-features" => PrintRequest::TargetFeatures, + "relocation-models" => PrintRequest::RelocationModels, + "code-models" => PrintRequest::CodeModels, + "tls-models" => PrintRequest::TlsModels, + "native-static-libs" => PrintRequest::NativeStaticLibs, + "target-spec-json" => { + if dopts.unstable_options { + PrintRequest::TargetSpec + } else { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to \ + enable the target-spec-json print option", + ); + } + } + req => early_error(error_format, &format!("unknown print request `{}`", req)), + })); + + prints +} + +fn parse_target_triple(matches: &getopts::Matches, error_format: ErrorOutputType) -> TargetTriple { + match matches.opt_str("target") { + Some(target) if target.ends_with(".json") => { + let path = Path::new(&target); + TargetTriple::from_path(&path).unwrap_or_else(|_| + early_error(error_format, &format!("target file {:?} does not exist", path))) + } + Some(target) => TargetTriple::TargetTriple(target), + _ => TargetTriple::from_triple(host_triple()), + } +} + +fn parse_opt_level( + matches: &getopts::Matches, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> OptLevel { + // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able + // to use them interchangeably. However, because they're technically different flags, + // we need to work out manually which should take precedence if both are supplied (i.e. + // the rightmost flag). We do this by finding the (rightmost) position of both flags and + // comparing them. Note that if a flag is not found, its position will be `None`, which + // always compared less than `Some(_)`. + let max_o = matches.opt_positions("O").into_iter().max(); + let max_c = matches.opt_strs_pos("C").into_iter().flat_map(|(i, s)| { + if let Some("opt-level") = s.splitn(2, '=').next() { + Some(i) + } else { + None + } + }).max(); + if max_o > max_c { + OptLevel::Default + } else { + match cg.opt_level.as_ref().map(String::as_ref) { + None => OptLevel::No, + Some("0") => OptLevel::No, + Some("1") => OptLevel::Less, + Some("2") => OptLevel::Default, + Some("3") => OptLevel::Aggressive, + Some("s") => OptLevel::Size, + Some("z") => OptLevel::SizeMin, + Some(arg) => { + early_error( + error_format, + &format!( + "optimization level needs to be \ + between 0-3, s or z (instead was `{}`)", + arg + ), + ); + } + } + } +} + +fn select_debuginfo( + matches: &getopts::Matches, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> DebugInfo { + let max_g = matches.opt_positions("g").into_iter().max(); + let max_c = matches.opt_strs_pos("C").into_iter().flat_map(|(i, s)| { + if let Some("debuginfo") = s.splitn(2, '=').next() { + Some(i) + } else { + None + } + }).max(); + if max_g > max_c { + DebugInfo::Full + } else { + match cg.debuginfo { + None | Some(0) => DebugInfo::None, + Some(1) => DebugInfo::Limited, + Some(2) => DebugInfo::Full, + Some(arg) => { + early_error( + error_format, + &format!( + "debug info level needs to be between \ + 0-2 (instead was `{}`)", + arg + ), + ); + } + } + } +} + +fn parse_libs( + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> Vec<(String, Option, Option)> { + matches + .opt_strs("l") + .into_iter() + .map(|s| { + // Parse string of the form "[KIND=]lib[:new_name]", + // where KIND is one of "dylib", "framework", "static". + let mut parts = s.splitn(2, '='); + let kind = parts.next().unwrap(); + let (name, kind) = match (parts.next(), kind) { + (None, name) => (name, None), + (Some(name), "dylib") => (name, Some(NativeLibraryKind::NativeUnknown)), + (Some(name), "framework") => (name, Some(NativeLibraryKind::NativeFramework)), + (Some(name), "static") => (name, Some(NativeLibraryKind::NativeStatic)), + (Some(name), "static-nobundle") => { + (name, Some(NativeLibraryKind::NativeStaticNobundle)) + } + (_, s) => { + early_error( + error_format, + &format!( + "unknown library kind `{}`, expected \ + one of dylib, framework, or static", + s + ), + ); + } + }; + if kind == Some(NativeLibraryKind::NativeStaticNobundle) && + !nightly_options::is_nightly_build() { + early_error( + error_format, + &format!( + "the library kind 'static-nobundle' is only \ + accepted on the nightly compiler" + ), + ); + } + let mut name_parts = name.splitn(2, ':'); + let name = name_parts.next().unwrap(); + let new_name = name_parts.next(); + (name.to_owned(), new_name.map(|n| n.to_owned()), kind) + }) + .collect() +} + +fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType) -> BorrowckMode { + match dopts.borrowck.as_ref().map(|s| &s[..]) { + None | Some("migrate") => BorrowckMode::Migrate, + Some("mir") => BorrowckMode::Mir, + Some(m) => early_error(error_format, &format!("unknown borrowck mode `{}`", m)), + } +} + +fn parse_externs( + matches: &getopts::Matches, + debugging_opts: &DebuggingOptions, + error_format: ErrorOutputType, +) -> Externs { + if matches.opt_present("extern-private") && !debugging_opts.unstable_options { + early_error( + ErrorOutputType::default(), + "'--extern-private' is unstable and only \ + available for nightly builds of rustc." + ) + } + + // We start out with a `Vec<(Option, bool)>>`, + // and later convert it into a `BTreeSet<(Option, bool)>` + // This allows to modify entries in-place to set their correct + // 'public' value. + let mut externs: BTreeMap = BTreeMap::new(); + for (arg, private) in matches.opt_strs("extern").into_iter().map(|v| (v, false)) + .chain(matches.opt_strs("extern-private").into_iter().map(|v| (v, true))) { + + let mut parts = arg.splitn(2, '='); + let name = parts.next().unwrap_or_else(|| + early_error(error_format, "--extern value must not be empty")); + let location = parts.next().map(|s| s.to_string()); + + let entry = externs + .entry(name.to_owned()) + .or_default(); + + + entry.locations.insert(location.clone()); + + // Crates start out being not private, + // and go to being private if we see an '--extern-private' + // flag + entry.is_private_dep |= private; + } + Externs(externs) +} + +fn parse_remap_path_prefix( + matches: &getopts::Matches, + error_format: ErrorOutputType +) -> Vec<(PathBuf, PathBuf)> { + matches + .opt_strs("remap-path-prefix") + .into_iter() + .map(|remap| { + let mut parts = remap.rsplitn(2, '='); // reverse iterator + let to = parts.next(); + let from = parts.next(); + match (from, to) { + (Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)), + _ => early_error( + error_format, + "--remap-path-prefix must contain '=' between FROM and TO", + ), + } + }) + .collect() +} + +pub fn build_session_options(matches: &getopts::Matches) -> Options { + let color = parse_color(matches); + + let edition = parse_crate_edition(matches); + + let (json_rendered, json_artifact_notifications) = parse_json(matches); + + let error_format = parse_error_format(matches, color, json_rendered); + + let unparsed_crate_types = matches.opt_strs("crate-type"); + let crate_types = parse_crate_types_from_list(unparsed_crate_types) + .unwrap_or_else(|e| early_error(error_format, &e[..])); + + let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); + + let mut debugging_opts = build_debugging_options(matches, error_format); + check_debug_option_stability(&debugging_opts, error_format, json_rendered); + + let output_types = parse_output_types(&debugging_opts, matches, error_format); + + let mut cg = build_codegen_options(matches, error_format); + let (disable_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( + &output_types, + matches, + error_format, + cg.codegen_units, + ); + + check_thread_count(&debugging_opts, error_format); + + let incremental = select_incremental_path(&debugging_opts, &cg, error_format); + + if debugging_opts.profile && incremental.is_some() { + early_error( + error_format, + "can't instrument with gcov profiling when compiling incrementally", + ); + } + + if cg.profile_generate.enabled() && cg.profile_use.is_some() { + early_error( + error_format, + "options `-C profile-generate` and `-C profile-use` are exclusive", + ); + } + + let prints = collect_print_requests( + &mut cg, + &mut debugging_opts, + matches, + error_format, + ); + + let cg = cg; + + let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); + let target_triple = parse_target_triple(matches, error_format); + let opt_level = parse_opt_level(matches, &cg, error_format); + // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able + // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) + // for more details. + let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No); + let debuginfo = select_debuginfo(matches, &cg, error_format); + + let mut search_paths = vec![]; + for s in &matches.opt_strs("L") { + search_paths.push(SearchPath::from_cli_opt(&s[..], error_format)); + } + + let libs = parse_libs(matches, error_format); + + let test = matches.opt_present("test"); + + let borrowck_mode = parse_borrowck_mode(&debugging_opts, error_format); + + if !cg.remark.is_empty() && debuginfo == DebugInfo::None { + early_warn( + error_format, + "-C remark requires \"-C debuginfo=n\" to show source locations", + ); + } + + let externs = parse_externs(matches, &debugging_opts, error_format); + + let crate_name = matches.opt_str("crate-name"); + + let remap_path_prefix = parse_remap_path_prefix(matches, error_format); + + let pretty = parse_pretty(matches, &debugging_opts, error_format); + + Options { + crate_types, + optimize: opt_level, + debuginfo, + lint_opts, + lint_cap, + describe_lints, + output_types, + search_paths, + maybe_sysroot: sysroot_opt, + target_triple, + test, + incremental, + debugging_opts, + prints, + borrowck_mode, + cg, + error_format, + externs, + crate_name, + alt_std_name: None, + libs, + unstable_features: UnstableFeatures::from_environment(), + debug_assertions, + actually_rustdoc: false, + cli_forced_codegen_units: codegen_units, + cli_forced_thinlto_off: disable_thinlto, + remap_path_prefix, + edition, + json_artifact_notifications, + pretty, + } +} + +fn parse_pretty( + matches: &getopts::Matches, + debugging_opts: &DebuggingOptions, + efmt: ErrorOutputType, +) -> Option { + let pretty = if debugging_opts.unstable_options { + matches.opt_default("pretty", "normal").map(|a| { + // stable pretty-print variants only + parse_pretty_inner(efmt, &a, false) + }) + } else { + None + }; + + return if pretty.is_none() { + debugging_opts.unpretty.as_ref().map(|a| { + // extended with unstable pretty-print variants + parse_pretty_inner(efmt, &a, true) + }) + } else { + pretty + }; + + fn parse_pretty_inner( + efmt: ErrorOutputType, + name: &str, + extended: bool, + ) -> PpMode { + use PpMode::*; + use PpSourceMode::*; + let first = match (name, extended) { + ("normal", _) => PpmSource(PpmNormal), + ("identified", _) => PpmSource(PpmIdentified), + ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops), + ("expanded", _) => PpmSource(PpmExpanded), + ("expanded,identified", _) => PpmSource(PpmExpandedIdentified), + ("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene), + ("hir", true) => PpmHir(PpmNormal), + ("hir,identified", true) => PpmHir(PpmIdentified), + ("hir,typed", true) => PpmHir(PpmTyped), + ("hir-tree", true) => PpmHirTree(PpmNormal), + ("mir", true) => PpmMir, + ("mir-cfg", true) => PpmMirCFG, + _ => { + if extended { + early_error(efmt, &format!("argument to `unpretty` must be one of `normal`, \ + `expanded`, `identified`, `expanded,identified`, \ + `expanded,hygiene`, `everybody_loops`, \ + `hir`, `hir,identified`, `hir,typed`, `hir-tree`, \ + `mir` or `mir-cfg`; got {}", + name)); + } else { + early_error(efmt, &format!("argument to `pretty` must be one of `normal`, \ + `expanded`, `identified`, or `expanded,identified`; got {}", + name)); + } + } + }; + first + } +} + +pub fn make_crate_type_option() -> RustcOptGroup { + opt::multi_s( + "", + "crate-type", + "Comma separated list of types of crates + for the compiler to emit", + "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]", + ) +} + +pub fn parse_crate_types_from_list(list_list: Vec) -> Result, String> { + let mut crate_types: Vec = Vec::new(); + for unparsed_crate_type in &list_list { + for part in unparsed_crate_type.split(',') { + let new_part = match part { + "lib" => default_lib_output(), + "rlib" => CrateType::Rlib, + "staticlib" => CrateType::Staticlib, + "dylib" => CrateType::Dylib, + "cdylib" => CrateType::Cdylib, + "bin" => CrateType::Executable, + "proc-macro" => CrateType::ProcMacro, + _ => return Err(format!("unknown crate type: `{}`", part)) + }; + if !crate_types.contains(&new_part) { + crate_types.push(new_part) + } + } + } + + Ok(crate_types) +} + +pub mod nightly_options { + use getopts; + use rustc_feature::UnstableFeatures; + use super::{ErrorOutputType, OptionStability, RustcOptGroup}; + use crate::early_error; + + pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { + is_nightly_build() + && matches + .opt_strs("Z") + .iter() + .any(|x| *x == "unstable-options") + } + + pub fn is_nightly_build() -> bool { + UnstableFeatures::from_environment().is_nightly_build() + } + + pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) { + let has_z_unstable_option = matches + .opt_strs("Z") + .iter() + .any(|x| *x == "unstable-options"); + let really_allows_unstable_options = + UnstableFeatures::from_environment().is_nightly_build(); + + for opt in flags.iter() { + if opt.stability == OptionStability::Stable { + continue; + } + if !matches.opt_present(opt.name) { + continue; + } + if opt.name != "Z" && !has_z_unstable_option { + early_error( + ErrorOutputType::default(), + &format!( + "the `-Z unstable-options` flag must also be passed to enable \ + the flag `{}`", + opt.name + ), + ); + } + if really_allows_unstable_options { + continue; + } + match opt.stability { + OptionStability::Unstable => { + let msg = format!( + "the option `{}` is only accepted on the \ + nightly compiler", + opt.name + ); + early_error(ErrorOutputType::default(), &msg); + } + OptionStability::Stable => {} + } + } + } +} + +impl fmt::Display for CrateType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + CrateType::Executable => "bin".fmt(f), + CrateType::Dylib => "dylib".fmt(f), + CrateType::Rlib => "rlib".fmt(f), + CrateType::Staticlib => "staticlib".fmt(f), + CrateType::Cdylib => "cdylib".fmt(f), + CrateType::ProcMacro => "proc-macro".fmt(f), + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PpSourceMode { + PpmNormal, + PpmEveryBodyLoops, + PpmExpanded, + PpmIdentified, + PpmExpandedIdentified, + PpmExpandedHygiene, + PpmTyped, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PpMode { + PpmSource(PpSourceMode), + PpmHir(PpSourceMode), + PpmHirTree(PpSourceMode), + PpmMir, + PpmMirCFG, +} + +impl PpMode { + pub fn needs_ast_map(&self) -> bool { + use PpMode::*; + use PpSourceMode::*; + match *self { + PpmSource(PpmNormal) | + PpmSource(PpmEveryBodyLoops) | + PpmSource(PpmIdentified) => false, + + PpmSource(PpmExpanded) | + PpmSource(PpmExpandedIdentified) | + PpmSource(PpmExpandedHygiene) | + PpmHir(_) | + PpmHirTree(_) | + PpmMir | + PpmMirCFG => true, + PpmSource(PpmTyped) => panic!("invalid state"), + } + } + + pub fn needs_analysis(&self) -> bool { + use PpMode::*; + match *self { + PpmMir | PpmMirCFG => true, + _ => false, + } + } +} + +/// Command-line arguments passed to the compiler have to be incorporated with +/// the dependency tracking system for incremental compilation. This module +/// provides some utilities to make this more convenient. +/// +/// The values of all command-line arguments that are relevant for dependency +/// tracking are hashed into a single value that determines whether the +/// incremental compilation cache can be re-used or not. This hashing is done +/// via the `DepTrackingHash` trait defined below, since the standard `Hash` +/// implementation might not be suitable (e.g., arguments are stored in a `Vec`, +/// the hash of which is order dependent, but we might not want the order of +/// arguments to make a difference for the hash). +/// +/// However, since the value provided by `Hash::hash` often *is* suitable, +/// especially for primitive types, there is the +/// `impl_dep_tracking_hash_via_hash!()` macro that allows to simply reuse the +/// `Hash` implementation for `DepTrackingHash`. It's important though that +/// we have an opt-in scheme here, so one is hopefully forced to think about +/// how the hash should be calculated when adding a new command-line argument. +mod dep_tracking { + use crate::lint; + use crate::utils::NativeLibraryKind; + use std::collections::BTreeMap; + use std::hash::Hash; + use std::path::PathBuf; + use std::collections::hash_map::DefaultHasher; + use super::{CrateType, DebugInfo, ErrorOutputType, OptLevel, OutputTypes, + Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath, + SymbolManglingVersion}; + use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel, TargetTriple}; + use syntax_pos::edition::Edition; + use rustc_feature::UnstableFeatures; + + pub trait DepTrackingHash { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType); + } + + macro_rules! impl_dep_tracking_hash_via_hash { + ($t:ty) => ( + impl DepTrackingHash for $t { + fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType) { + Hash::hash(self, hasher); + } + } + ) + } + + macro_rules! impl_dep_tracking_hash_for_sortable_vec_of { + ($t:ty) => ( + impl DepTrackingHash for Vec<$t> { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { + let mut elems: Vec<&$t> = self.iter().collect(); + elems.sort(); + Hash::hash(&elems.len(), hasher); + for (index, elem) in elems.iter().enumerate() { + Hash::hash(&index, hasher); + DepTrackingHash::hash(*elem, hasher, error_format); + } + } + } + ); + } + + impl_dep_tracking_hash_via_hash!(bool); + impl_dep_tracking_hash_via_hash!(usize); + impl_dep_tracking_hash_via_hash!(u64); + impl_dep_tracking_hash_via_hash!(String); + impl_dep_tracking_hash_via_hash!(PathBuf); + impl_dep_tracking_hash_via_hash!(lint::Level); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); + impl_dep_tracking_hash_via_hash!(Option>); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(CrateType); + impl_dep_tracking_hash_via_hash!(MergeFunctions); + impl_dep_tracking_hash_via_hash!(PanicStrategy); + impl_dep_tracking_hash_via_hash!(RelroLevel); + impl_dep_tracking_hash_via_hash!(Passes); + impl_dep_tracking_hash_via_hash!(OptLevel); + impl_dep_tracking_hash_via_hash!(LtoCli); + impl_dep_tracking_hash_via_hash!(DebugInfo); + impl_dep_tracking_hash_via_hash!(UnstableFeatures); + impl_dep_tracking_hash_via_hash!(OutputTypes); + impl_dep_tracking_hash_via_hash!(NativeLibraryKind); + impl_dep_tracking_hash_via_hash!(Sanitizer); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(TargetTriple); + impl_dep_tracking_hash_via_hash!(Edition); + impl_dep_tracking_hash_via_hash!(LinkerPluginLto); + impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); + impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); + + impl_dep_tracking_hash_for_sortable_vec_of!(String); + impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); + impl_dep_tracking_hash_for_sortable_vec_of!(CrateType); + impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level)); + impl_dep_tracking_hash_for_sortable_vec_of!(( + String, + Option, + Option + )); + impl_dep_tracking_hash_for_sortable_vec_of!((String, u64)); + impl_dep_tracking_hash_for_sortable_vec_of!(Sanitizer); + + impl DepTrackingHash for (T1, T2) + where + T1: DepTrackingHash, + T2: DepTrackingHash, + { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { + Hash::hash(&0, hasher); + DepTrackingHash::hash(&self.0, hasher, error_format); + Hash::hash(&1, hasher); + DepTrackingHash::hash(&self.1, hasher, error_format); + } + } + + impl DepTrackingHash for (T1, T2, T3) + where + T1: DepTrackingHash, + T2: DepTrackingHash, + T3: DepTrackingHash, + { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { + Hash::hash(&0, hasher); + DepTrackingHash::hash(&self.0, hasher, error_format); + Hash::hash(&1, hasher); + DepTrackingHash::hash(&self.1, hasher, error_format); + Hash::hash(&2, hasher); + DepTrackingHash::hash(&self.2, hasher, error_format); + } + } + + // This is a stable hash because BTreeMap is a sorted container + pub fn stable_hash( + sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, + hasher: &mut DefaultHasher, + error_format: ErrorOutputType, + ) { + for (key, sub_hash) in sub_hashes { + // Using Hash::hash() instead of DepTrackingHash::hash() is fine for + // the keys, as they are just plain strings + Hash::hash(&key.len(), hasher); + Hash::hash(key, hasher); + sub_hash.hash(hasher, error_format); + } + } +} diff --git a/src/librustc_session/filesearch.rs b/src/librustc_session/filesearch.rs new file mode 100644 index 00000000000..a0bdc6a3254 --- /dev/null +++ b/src/librustc_session/filesearch.rs @@ -0,0 +1,174 @@ +#![allow(non_camel_case_types)] + +pub use self::FileMatch::*; + +use std::borrow::Cow; +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; + +use crate::search_paths::{SearchPath, PathKind}; +use rustc_fs_util::fix_windows_verbatim_for_gcc; +use log::debug; + +#[derive(Copy, Clone)] +pub enum FileMatch { + FileMatches, + FileDoesntMatch, +} + +// A module for searching for libraries + +#[derive(Clone)] +pub struct FileSearch<'a> { + sysroot: &'a Path, + triple: &'a str, + search_paths: &'a [SearchPath], + tlib_path: &'a SearchPath, + kind: PathKind, +} + +impl<'a> FileSearch<'a> { + pub fn search_paths(&self) -> impl Iterator { + let kind = self.kind; + self.search_paths.iter() + .filter(move |sp| sp.kind.matches(kind)) + .chain(std::iter::once(self.tlib_path)) + } + + pub fn get_lib_path(&self) -> PathBuf { + make_target_lib_path(self.sysroot, self.triple) + } + + pub fn search(&self, mut pick: F) + where F: FnMut(&Path, PathKind) -> FileMatch + { + for search_path in self.search_paths() { + debug!("searching {}", search_path.dir.display()); + fn is_rlib(p: &Path) -> bool { + p.extension() == Some("rlib".as_ref()) + } + // Reading metadata out of rlibs is faster, and if we find both + // an rlib and a dylib we only read one of the files of + // metadata, so in the name of speed, bring all rlib files to + // the front of the search list. + let files1 = search_path.files.iter().filter(|p| is_rlib(p)); + let files2 = search_path.files.iter().filter(|p| !is_rlib(p)); + for path in files1.chain(files2) { + debug!("testing {}", path.display()); + let maybe_picked = pick(path, search_path.kind); + match maybe_picked { + FileMatches => { + debug!("picked {}", path.display()); + } + FileDoesntMatch => { + debug!("rejected {}", path.display()); + } + } + } + } + } + + pub fn new(sysroot: &'a Path, + triple: &'a str, + search_paths: &'a Vec, + tlib_path: &'a SearchPath, + kind: PathKind) + -> FileSearch<'a> { + debug!("using sysroot = {}, triple = {}", sysroot.display(), triple); + FileSearch { + sysroot, + triple, + search_paths, + tlib_path, + kind, + } + } + + // Returns just the directories within the search paths. + pub fn search_path_dirs(&self) -> Vec { + self.search_paths() + .map(|sp| sp.dir.to_path_buf()) + .collect() + } + + // Returns a list of directories where target-specific tool binaries are located. + pub fn get_tools_search_paths(&self) -> Vec { + let mut p = PathBuf::from(self.sysroot); + p.push(find_libdir(self.sysroot).as_ref()); + p.push(RUST_LIB_DIR); + p.push(&self.triple); + p.push("bin"); + vec![p] + } +} + +pub fn relative_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { + let mut p = PathBuf::from(find_libdir(sysroot).as_ref()); + assert!(p.is_relative()); + p.push(RUST_LIB_DIR); + p.push(target_triple); + p.push("lib"); + p +} + +pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { + sysroot.join(&relative_target_lib_path(sysroot, target_triple)) +} + +pub fn get_or_default_sysroot() -> PathBuf { + // Follow symlinks. If the resolved path is relative, make it absolute. + fn canonicalize(path: Option) -> Option { + path.and_then(|path| { + match fs::canonicalize(&path) { + // See comments on this target function, but the gist is that + // gcc chokes on verbatim paths which fs::canonicalize generates + // so we try to avoid those kinds of paths. + Ok(canon) => Some(fix_windows_verbatim_for_gcc(&canon)), + Err(e) => panic!("failed to get realpath: {}", e), + } + }) + } + + match env::current_exe() { + Ok(exe) => { + match canonicalize(Some(exe)) { + Some(mut p) => { p.pop(); p.pop(); p }, + None => panic!("can't determine value for sysroot") + } + } + Err(ref e) => panic!(format!("failed to get current_exe: {}", e)) + } +} + +// The name of the directory rustc expects libraries to be located. +fn find_libdir(sysroot: &Path) -> Cow<'static, str> { + // FIXME: This is a quick hack to make the rustc binary able to locate + // Rust libraries in Linux environments where libraries might be installed + // to lib64/lib32. This would be more foolproof by basing the sysroot off + // of the directory where librustc is located, rather than where the rustc + // binary is. + // If --libdir is set during configuration to the value other than + // "lib" (i.e., non-default), this value is used (see issue #16552). + + #[cfg(target_pointer_width = "64")] + const PRIMARY_LIB_DIR: &str = "lib64"; + + #[cfg(target_pointer_width = "32")] + const PRIMARY_LIB_DIR: &str = "lib32"; + + const SECONDARY_LIB_DIR: &str = "lib"; + + match option_env!("CFG_LIBDIR_RELATIVE") { + Some(libdir) if libdir != "lib" => libdir.into(), + _ => if sysroot.join(PRIMARY_LIB_DIR).join(RUST_LIB_DIR).exists() { + PRIMARY_LIB_DIR.into() + } else { + SECONDARY_LIB_DIR.into() + } + } +} + +// The name of rustc's own place to organize libraries. +// Used to be "rustc", now the default is "rustlib" +const RUST_LIB_DIR: &str = "rustlib"; diff --git a/src/librustc_session/lib.rs b/src/librustc_session/lib.rs index d5ccb97a7f9..9d7c23100a0 100644 --- a/src/librustc_session/lib.rs +++ b/src/librustc_session/lib.rs @@ -1,6 +1,21 @@ +#![feature(test)] + +// Use the test crate here so we depend on getopts through it. This allow tools to link to both +// librustc_session and libtest. +extern crate test as _; +extern crate getopts; + pub mod cgu_reuse_tracker; pub mod utils; #[macro_use] pub mod lint; pub mod node_id; pub mod parse; + +mod code_stats; +pub mod config; +pub mod filesearch; +pub mod search_paths; + +mod session; +pub use session::*; diff --git a/src/librustc_session/search_paths.rs b/src/librustc_session/search_paths.rs new file mode 100644 index 00000000000..b9f2ae7618b --- /dev/null +++ b/src/librustc_session/search_paths.rs @@ -0,0 +1,74 @@ +use std::path::{Path, PathBuf}; +use crate::{early_error, config}; +use crate::filesearch::make_target_lib_path; + +#[derive(Clone, Debug)] +pub struct SearchPath { + pub kind: PathKind, + pub dir: PathBuf, + pub files: Vec, +} + +#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq)] +pub enum PathKind { + Native, + Crate, + Dependency, + Framework, + ExternFlag, + All, +} + +rustc_data_structures::impl_stable_hash_via_hash!(PathKind); + +impl PathKind { + pub fn matches(&self, kind: PathKind) -> bool { + match (self, kind) { + (PathKind::All, _) | (_, PathKind::All) => true, + _ => *self == kind, + } + } +} + +impl SearchPath { + pub fn from_cli_opt(path: &str, output: config::ErrorOutputType) -> Self { + let (kind, path) = if path.starts_with("native=") { + (PathKind::Native, &path["native=".len()..]) + } else if path.starts_with("crate=") { + (PathKind::Crate, &path["crate=".len()..]) + } else if path.starts_with("dependency=") { + (PathKind::Dependency, &path["dependency=".len()..]) + } else if path.starts_with("framework=") { + (PathKind::Framework, &path["framework=".len()..]) + } else if path.starts_with("all=") { + (PathKind::All, &path["all=".len()..]) + } else { + (PathKind::All, path) + }; + if path.is_empty() { + early_error(output, "empty search path given via `-L`"); + } + + let dir = PathBuf::from(path); + Self::new(kind, dir) + } + + pub fn from_sysroot_and_triple(sysroot: &Path, triple: &str) -> Self { + Self::new(PathKind::All, make_target_lib_path(sysroot, triple)) + } + + fn new(kind: PathKind, dir: PathBuf) -> Self { + // Get the files within the directory. + let files = match std::fs::read_dir(&dir) { + Ok(files) => { + files.filter_map(|p| { + p.ok().map(|s| s.path()) + }) + .collect::>() + } + Err(..) => vec![], + }; + + SearchPath { kind, dir, files } + } +} diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs new file mode 100644 index 00000000000..9369c1771a3 --- /dev/null +++ b/src/librustc_session/session.rs @@ -0,0 +1,1299 @@ +pub use crate::code_stats::{DataTypeKind, SizeKind, FieldInfo, VariantInfo}; +use crate::code_stats::CodeStats; + +use crate::cgu_reuse_tracker::CguReuseTracker; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; + +use crate::lint; +use crate::filesearch; +use crate::config::{self, OutputType, PrintRequest, Sanitizer, SwitchWithOptPath}; +use crate::search_paths::{PathKind, SearchPath}; +use crate::utils::duration_to_secs_str; +use rustc_errors::ErrorReported; + +use rustc_data_structures::base_n; +use rustc_data_structures::sync::{ + self, Lrc, Lock, OneThread, Once, AtomicU64, AtomicUsize, Ordering, + Ordering::SeqCst, +}; +use rustc_data_structures::impl_stable_hash_via_hash; + +use rustc_errors::{DiagnosticBuilder, DiagnosticId, Applicability}; +use rustc_errors::emitter::{Emitter, EmitterWriter}; +use rustc_errors::emitter::HumanReadableErrorType; +use rustc_errors::annotate_snippet_emitter_writer::{AnnotateSnippetEmitterWriter}; +use syntax_pos::edition::Edition; +use rustc_errors::json::JsonEmitter; +use syntax_pos::source_map; +use crate::parse::ParseSess; +use syntax_pos::{MultiSpan, Span}; + +use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple}; +use rustc_data_structures::flock; +use rustc_data_structures::jobserver::{self, Client}; +use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; + +use std; +use std::cell::{self, RefCell}; +use std::env; +use std::fmt; +use std::io::Write; +use std::num::NonZeroU32; +use std::path::PathBuf; +use std::time::Duration; +use std::sync::Arc; + +pub struct OptimizationFuel { + /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`. + remaining: u64, + /// We're rejecting all further optimizations. + out_of_fuel: bool, +} + +/// Represents the data associated with a compilation +/// session for a single crate. +pub struct Session { + pub target: config::Config, + pub host: Target, + pub opts: config::Options, + pub host_tlib_path: SearchPath, + /// `None` if the host and target are the same. + pub target_tlib_path: Option, + pub parse_sess: ParseSess, + pub sysroot: PathBuf, + /// The name of the root source file of the crate, in the local file system. + /// `None` means that there is no source file. + pub local_crate_source_file: Option, + /// The directory the compiler has been executed in plus a flag indicating + /// if the value stored here has been affected by path remapping. + pub working_dir: (PathBuf, bool), + + /// Set of `(DiagnosticId, Option, message)` tuples tracking + /// (sub)diagnostics that have been set once, but should not be set again, + /// in order to avoid redundantly verbose output (Issue #24690, #44953). + pub one_time_diagnostics: Lock, String)>>, + pub crate_types: Once>, + /// The `crate_disambiguator` is constructed out of all the `-C metadata` + /// arguments passed to the compiler. Its value together with the crate-name + /// forms a unique global identifier for the crate. It is used to allow + /// multiple crates with the same name to coexist. See the + /// `rustc_codegen_llvm::back::symbol_names` module for more information. + pub crate_disambiguator: Once, + + features: Once, + + /// The maximum recursion limit for potentially infinitely recursive + /// operations such as auto-dereference and monomorphization. + pub recursion_limit: Once, + + /// The maximum length of types during monomorphization. + pub type_length_limit: Once, + + /// Map from imported macro spans (which consist of + /// the localized span for the macro body) to the + /// macro name and definition span in the source crate. + pub imported_macro_spans: OneThread>>, + + incr_comp_session: OneThread>, + /// Used for incremental compilation tests. Will only be populated if + /// `-Zquery-dep-graph` is specified. + pub cgu_reuse_tracker: CguReuseTracker, + + /// Used by `-Z self-profile`. + pub prof: SelfProfilerRef, + + /// Some measurements that are being gathered during compilation. + pub perf_stats: PerfStats, + + /// Data about code being compiled, gathered during compilation. + pub code_stats: CodeStats, + + /// If `-zfuel=crate=n` is specified, `Some(crate)`. + optimization_fuel_crate: Option, + + /// Tracks fuel info if `-zfuel=crate=n` is specified. + optimization_fuel: Lock, + + // The next two are public because the driver needs to read them. + /// If `-zprint-fuel=crate`, `Some(crate)`. + pub print_fuel_crate: Option, + /// Always set to zero and incremented so that we can print fuel expended by a crate. + pub print_fuel: AtomicU64, + + /// Loaded up early on in the initialization of this `Session` to avoid + /// false positives about a job server in our environment. + pub jobserver: Client, + + /// Cap lint level specified by a driver specifically. + pub driver_lint_caps: FxHashMap, + + /// `Span`s of trait methods that weren't found to avoid emitting object safety errors + pub trait_methods_not_found: Lock>, + + /// Mapping from ident span to path span for paths that don't exist as written, but that + /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. + pub confused_type_with_std_module: Lock>, +} + +pub struct PerfStats { + /// The accumulated time spent on computing symbol hashes. + pub symbol_hash_time: Lock, + /// The accumulated time spent decoding def path tables from metadata. + pub decode_def_path_tables_time: Lock, + /// Total number of values canonicalized queries constructed. + pub queries_canonicalized: AtomicUsize, + /// Number of times this query is invoked. + pub normalize_ty_after_erasing_regions: AtomicUsize, + /// Number of times this query is invoked. + pub normalize_projection_ty: AtomicUsize, +} + +/// Enum to support dispatch of one-time diagnostics (in `Session.diag_once`). +enum DiagnosticBuilderMethod { + Note, + SpanNote, + SpanSuggestion(String), // suggestion + // Add more variants as needed to support one-time diagnostics. +} + +/// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid +/// emitting the same message more than once. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum DiagnosticMessageId { + ErrorId(u16), // EXXXX error code as integer + LintId(lint::LintId), + StabilityId(Option), // issue number +} + +impl From<&'static lint::Lint> for DiagnosticMessageId { + fn from(lint: &'static lint::Lint) -> Self { + DiagnosticMessageId::LintId(lint::LintId::of(lint)) + } +} + +impl Session { + pub fn local_crate_disambiguator(&self) -> CrateDisambiguator { + *self.crate_disambiguator.get() + } + + pub fn struct_span_warn>( + &self, + sp: S, + msg: &str, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_warn(sp, msg) + } + pub fn struct_span_warn_with_code>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_warn_with_code(sp, msg, code) + } + pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_warn(msg) + } + pub fn struct_span_err>( + &self, + sp: S, + msg: &str, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_err(sp, msg) + } + pub fn struct_span_err_with_code>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_err_with_code(sp, msg, code) + } + // FIXME: This method should be removed (every error should have an associated error code). + pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_err(msg) + } + pub fn struct_err_with_code( + &self, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_err_with_code(msg, code) + } + pub fn struct_span_fatal>( + &self, + sp: S, + msg: &str, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_fatal(sp, msg) + } + pub fn struct_span_fatal_with_code>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_fatal_with_code(sp, msg, code) + } + pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_fatal(msg) + } + + pub fn span_fatal>(&self, sp: S, msg: &str) -> ! { + self.diagnostic().span_fatal(sp, msg).raise() + } + pub fn span_fatal_with_code>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> ! { + self.diagnostic() + .span_fatal_with_code(sp, msg, code) + .raise() + } + pub fn fatal(&self, msg: &str) -> ! { + self.diagnostic().fatal(msg).raise() + } + pub fn span_err_or_warn>(&self, is_warning: bool, sp: S, msg: &str) { + if is_warning { + self.span_warn(sp, msg); + } else { + self.span_err(sp, msg); + } + } + pub fn span_err>(&self, sp: S, msg: &str) { + self.diagnostic().span_err(sp, msg) + } + pub fn span_err_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { + self.diagnostic().span_err_with_code(sp, &msg, code) + } + pub fn err(&self, msg: &str) { + self.diagnostic().err(msg) + } + pub fn err_count(&self) -> usize { + self.diagnostic().err_count() + } + pub fn has_errors(&self) -> bool { + self.diagnostic().has_errors() + } + pub fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.diagnostic().has_errors_or_delayed_span_bugs() + } + pub fn abort_if_errors(&self) { + self.diagnostic().abort_if_errors(); + } + pub fn compile_status(&self) -> Result<(), ErrorReported> { + if self.has_errors() { + self.diagnostic().emit_stashed_diagnostics(); + Err(ErrorReported) + } else { + Ok(()) + } + } + // FIXME(matthewjasper) Remove this method, it should never be needed. + pub fn track_errors(&self, f: F) -> Result + where + F: FnOnce() -> T, + { + let old_count = self.err_count(); + let result = f(); + let errors = self.err_count() - old_count; + if errors == 0 { + Ok(result) + } else { + Err(ErrorReported) + } + } + pub fn span_warn>(&self, sp: S, msg: &str) { + self.diagnostic().span_warn(sp, msg) + } + pub fn span_warn_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { + self.diagnostic().span_warn_with_code(sp, msg, code) + } + pub fn warn(&self, msg: &str) { + self.diagnostic().warn(msg) + } + pub fn opt_span_warn>(&self, opt_sp: Option, msg: &str) { + match opt_sp { + Some(sp) => self.span_warn(sp, msg), + None => self.warn(msg), + } + } + /// Delay a span_bug() call until abort_if_errors() + pub fn delay_span_bug>(&self, sp: S, msg: &str) { + self.diagnostic().delay_span_bug(sp, msg) + } + pub fn note_without_error(&self, msg: &str) { + self.diagnostic().note_without_error(msg) + } + pub fn span_note_without_error>(&self, sp: S, msg: &str) { + self.diagnostic().span_note_without_error(sp, msg) + } + + pub fn diagnostic(&self) -> &rustc_errors::Handler { + &self.parse_sess.span_diagnostic + } + + /// Analogous to calling methods on the given `DiagnosticBuilder`, but + /// deduplicates on lint ID, span (if any), and message for this `Session` + fn diag_once<'a, 'b>( + &'a self, + diag_builder: &'b mut DiagnosticBuilder<'a>, + method: DiagnosticBuilderMethod, + msg_id: DiagnosticMessageId, + message: &str, + span_maybe: Option, + ) { + let id_span_message = (msg_id, span_maybe, message.to_owned()); + let fresh = self.one_time_diagnostics + .borrow_mut() + .insert(id_span_message); + if fresh { + match method { + DiagnosticBuilderMethod::Note => { + diag_builder.note(message); + } + DiagnosticBuilderMethod::SpanNote => { + let span = span_maybe.expect("`span_note` needs a span"); + diag_builder.span_note(span, message); + } + DiagnosticBuilderMethod::SpanSuggestion(suggestion) => { + let span = span_maybe.expect("`span_suggestion_*` needs a span"); + diag_builder.span_suggestion( + span, + message, + suggestion, + Applicability::Unspecified, + ); + } + } + } + } + + pub fn diag_span_note_once<'a, 'b>( + &'a self, + diag_builder: &'b mut DiagnosticBuilder<'a>, + msg_id: DiagnosticMessageId, + span: Span, + message: &str, + ) { + self.diag_once( + diag_builder, + DiagnosticBuilderMethod::SpanNote, + msg_id, + message, + Some(span), + ); + } + + pub fn diag_note_once<'a, 'b>( + &'a self, + diag_builder: &'b mut DiagnosticBuilder<'a>, + msg_id: DiagnosticMessageId, + message: &str, + ) { + self.diag_once( + diag_builder, + DiagnosticBuilderMethod::Note, + msg_id, + message, + None, + ); + } + + pub fn diag_span_suggestion_once<'a, 'b>( + &'a self, + diag_builder: &'b mut DiagnosticBuilder<'a>, + msg_id: DiagnosticMessageId, + span: Span, + message: &str, + suggestion: String, + ) { + self.diag_once( + diag_builder, + DiagnosticBuilderMethod::SpanSuggestion(suggestion), + msg_id, + message, + Some(span), + ); + } + + pub fn source_map(&self) -> &source_map::SourceMap { + self.parse_sess.source_map() + } + pub fn verbose(&self) -> bool { + self.opts.debugging_opts.verbose + } + pub fn time_passes(&self) -> bool { + self.opts.debugging_opts.time_passes || self.opts.debugging_opts.time + } + pub fn time_extended(&self) -> bool { + self.opts.debugging_opts.time_passes + } + pub fn instrument_mcount(&self) -> bool { + self.opts.debugging_opts.instrument_mcount + } + pub fn time_llvm_passes(&self) -> bool { + self.opts.debugging_opts.time_llvm_passes + } + pub fn meta_stats(&self) -> bool { + self.opts.debugging_opts.meta_stats + } + pub fn asm_comments(&self) -> bool { + self.opts.debugging_opts.asm_comments + } + pub fn verify_llvm_ir(&self) -> bool { + self.opts.debugging_opts.verify_llvm_ir + || cfg!(always_verify_llvm_ir) + } + pub fn borrowck_stats(&self) -> bool { + self.opts.debugging_opts.borrowck_stats + } + pub fn print_llvm_passes(&self) -> bool { + self.opts.debugging_opts.print_llvm_passes + } + pub fn binary_dep_depinfo(&self) -> bool { + self.opts.debugging_opts.binary_dep_depinfo + } + + /// Gets the features enabled for the current compilation session. + /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents + /// dependency tracking. Use tcx.features() instead. + #[inline] + pub fn features_untracked(&self) -> &rustc_feature::Features { + self.features.get() + } + + pub fn init_features(&self, features: rustc_feature::Features) { + self.features.set(features); + } + + /// Calculates the flavor of LTO to use for this compilation. + pub fn lto(&self) -> config::Lto { + // If our target has codegen requirements ignore the command line + if self.target.target.options.requires_lto { + return config::Lto::Fat; + } + + // If the user specified something, return that. If they only said `-C + // lto` and we've for whatever reason forced off ThinLTO via the CLI, + // then ensure we can't use a ThinLTO. + match self.opts.cg.lto { + config::LtoCli::Unspecified => { + // The compiler was invoked without the `-Clto` flag. Fall + // through to the default handling + } + config::LtoCli::No => { + // The user explicitly opted out of any kind of LTO + return config::Lto::No; + } + config::LtoCli::Yes | + config::LtoCli::Fat | + config::LtoCli::NoParam => { + // All of these mean fat LTO + return config::Lto::Fat; + } + config::LtoCli::Thin => { + return if self.opts.cli_forced_thinlto_off { + config::Lto::Fat + } else { + config::Lto::Thin + }; + } + } + + // Ok at this point the target doesn't require anything and the user + // hasn't asked for anything. Our next decision is whether or not + // we enable "auto" ThinLTO where we use multiple codegen units and + // then do ThinLTO over those codegen units. The logic below will + // either return `No` or `ThinLocal`. + + // If processing command line options determined that we're incompatible + // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option. + if self.opts.cli_forced_thinlto_off { + return config::Lto::No; + } + + // If `-Z thinlto` specified process that, but note that this is mostly + // a deprecated option now that `-C lto=thin` exists. + if let Some(enabled) = self.opts.debugging_opts.thinlto { + if enabled { + return config::Lto::ThinLocal; + } else { + return config::Lto::No; + } + } + + // If there's only one codegen unit and LTO isn't enabled then there's + // no need for ThinLTO so just return false. + if self.codegen_units() == 1 { + return config::Lto::No; + } + + // Now we're in "defaults" territory. By default we enable ThinLTO for + // optimized compiles (anything greater than O0). + match self.opts.optimize { + config::OptLevel::No => config::Lto::No, + _ => config::Lto::ThinLocal, + } + } + + /// Returns the panic strategy for this compile session. If the user explicitly selected one + /// using '-C panic', use that, otherwise use the panic strategy defined by the target. + pub fn panic_strategy(&self) -> PanicStrategy { + self.opts + .cg + .panic + .unwrap_or(self.target.target.options.panic_strategy) + } + pub fn fewer_names(&self) -> bool { + let more_names = self.opts + .output_types + .contains_key(&OutputType::LlvmAssembly) + || self.opts.output_types.contains_key(&OutputType::Bitcode); + + // Address sanitizer and memory sanitizer use alloca name when reporting an issue. + let more_names = match self.opts.debugging_opts.sanitizer { + Some(Sanitizer::Address) => true, + Some(Sanitizer::Memory) => true, + _ => more_names, + }; + + self.opts.debugging_opts.fewer_names || !more_names + } + + pub fn no_landing_pads(&self) -> bool { + self.opts.debugging_opts.no_landing_pads || self.panic_strategy() == PanicStrategy::Abort + } + pub fn unstable_options(&self) -> bool { + self.opts.debugging_opts.unstable_options + } + pub fn overflow_checks(&self) -> bool { + self.opts + .cg + .overflow_checks + .or(self.opts.debugging_opts.force_overflow_checks) + .unwrap_or(self.opts.debug_assertions) + } + + pub fn crt_static(&self) -> bool { + // If the target does not opt in to crt-static support, use its default. + if self.target.target.options.crt_static_respected { + self.crt_static_feature() + } else { + self.target.target.options.crt_static_default + } + } + + pub fn crt_static_feature(&self) -> bool { + let requested_features = self.opts.cg.target_feature.split(','); + let found_negative = requested_features.clone().any(|r| r == "-crt-static"); + let found_positive = requested_features.clone().any(|r| r == "+crt-static"); + + // If the target we're compiling for requests a static crt by default, + // then see if the `-crt-static` feature was passed to disable that. + // Otherwise if we don't have a static crt by default then see if the + // `+crt-static` feature was passed. + if self.target.target.options.crt_static_default { + !found_negative + } else { + found_positive + } + } + + pub fn must_not_eliminate_frame_pointers(&self) -> bool { + // "mcount" function relies on stack pointer. + // See . + if self.instrument_mcount() { + true + } else if let Some(x) = self.opts.cg.force_frame_pointers { + x + } else { + !self.target.target.options.eliminate_frame_pointer + } + } + + /// Returns the symbol name for the registrar function, + /// given the crate `Svh` and the function `DefIndex`. + pub fn generate_plugin_registrar_symbol(&self, disambiguator: CrateDisambiguator) -> String { + format!( + "__rustc_plugin_registrar_{}__", + disambiguator.to_fingerprint().to_hex() + ) + } + + pub fn generate_proc_macro_decls_symbol(&self, disambiguator: CrateDisambiguator) -> String { + format!( + "__rustc_proc_macro_decls_{}__", + disambiguator.to_fingerprint().to_hex() + ) + } + + pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { + filesearch::FileSearch::new( + &self.sysroot, + self.opts.target_triple.triple(), + &self.opts.search_paths, + // `target_tlib_path == None` means it's the same as `host_tlib_path`. + self.target_tlib_path.as_ref().unwrap_or(&self.host_tlib_path), + kind, + ) + } + pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { + filesearch::FileSearch::new( + &self.sysroot, + config::host_triple(), + &self.opts.search_paths, + &self.host_tlib_path, + kind, + ) + } + + pub fn set_incr_session_load_dep_graph(&self, load: bool) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::Active { ref mut load_dep_graph, .. } = *incr_comp_session { + *load_dep_graph = load; + } + } + + pub fn incr_session_load_dep_graph(&self) -> bool { + let incr_comp_session = self.incr_comp_session.borrow(); + match *incr_comp_session { + IncrCompSession::Active { load_dep_graph, .. } => load_dep_graph, + _ => false, + } + } + + pub fn init_incr_comp_session( + &self, + session_dir: PathBuf, + lock_file: flock::Lock, + load_dep_graph: bool, + ) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::NotInitialized = *incr_comp_session { + } else { + panic!( + "Trying to initialize IncrCompSession `{:?}`", + *incr_comp_session + ) + } + + *incr_comp_session = IncrCompSession::Active { + session_directory: session_dir, + lock_file, + load_dep_graph, + }; + } + + pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::Active { .. } = *incr_comp_session { + } else { + panic!( + "trying to finalize `IncrCompSession` `{:?}`", + *incr_comp_session + ); + } + + // Note: this will also drop the lock file, thus unlocking the directory. + *incr_comp_session = IncrCompSession::Finalized { + session_directory: new_directory_path, + }; + } + + pub fn mark_incr_comp_session_as_invalid(&self) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + let session_directory = match *incr_comp_session { + IncrCompSession::Active { + ref session_directory, + .. + } => session_directory.clone(), + IncrCompSession::InvalidBecauseOfErrors { .. } => return, + _ => panic!( + "trying to invalidate `IncrCompSession` `{:?}`", + *incr_comp_session + ), + }; + + // Note: this will also drop the lock file, thus unlocking the directory. + *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { + session_directory, + }; + } + + pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> { + let incr_comp_session = self.incr_comp_session.borrow(); + cell::Ref::map( + incr_comp_session, + |incr_comp_session| match *incr_comp_session { + IncrCompSession::NotInitialized => panic!( + "trying to get session directory from `IncrCompSession`: {:?}", + *incr_comp_session, + ), + IncrCompSession::Active { + ref session_directory, + .. + } + | IncrCompSession::Finalized { + ref session_directory, + } + | IncrCompSession::InvalidBecauseOfErrors { + ref session_directory, + } => session_directory, + }, + ) + } + + pub fn incr_comp_session_dir_opt(&self) -> Option> { + if self.opts.incremental.is_some() { + Some(self.incr_comp_session_dir()) + } else { + None + } + } + + pub fn print_perf_stats(&self) { + println!( + "Total time spent computing symbol hashes: {}", + duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) + ); + println!( + "Total time spent decoding DefPath tables: {}", + duration_to_secs_str(*self.perf_stats.decode_def_path_tables_time.lock()) + ); + println!("Total queries canonicalized: {}", + self.perf_stats.queries_canonicalized.load(Ordering::Relaxed)); + println!("normalize_ty_after_erasing_regions: {}", + self.perf_stats.normalize_ty_after_erasing_regions.load(Ordering::Relaxed)); + println!("normalize_projection_ty: {}", + self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed)); + } + + /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. + /// This expends fuel if applicable, and records fuel if applicable. + pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { + let mut ret = true; + if let Some(ref c) = self.optimization_fuel_crate { + if c == crate_name { + assert_eq!(self.threads(), 1); + let mut fuel = self.optimization_fuel.lock(); + ret = fuel.remaining != 0; + if fuel.remaining == 0 && !fuel.out_of_fuel { + eprintln!("optimization-fuel-exhausted: {}", msg()); + fuel.out_of_fuel = true; + } else if fuel.remaining > 0 { + fuel.remaining -= 1; + } + } + } + if let Some(ref c) = self.print_fuel_crate { + if c == crate_name { + assert_eq!(self.threads(), 1); + self.print_fuel.fetch_add(1, SeqCst); + } + } + ret + } + + /// Returns the number of query threads that should be used for this + /// compilation + pub fn threads(&self) -> usize { + self.opts.debugging_opts.threads + } + + /// Returns the number of codegen units that should be used for this + /// compilation + pub fn codegen_units(&self) -> usize { + if let Some(n) = self.opts.cli_forced_codegen_units { + return n; + } + if let Some(n) = self.target.target.options.default_codegen_units { + return n as usize; + } + + // Why is 16 codegen units the default all the time? + // + // The main reason for enabling multiple codegen units by default is to + // leverage the ability for the codegen backend to do codegen and + // optimization in parallel. This allows us, especially for large crates, to + // make good use of all available resources on the machine once we've + // hit that stage of compilation. Large crates especially then often + // take a long time in codegen/optimization and this helps us amortize that + // cost. + // + // Note that a high number here doesn't mean that we'll be spawning a + // large number of threads in parallel. The backend of rustc contains + // global rate limiting through the `jobserver` crate so we'll never + // overload the system with too much work, but rather we'll only be + // optimizing when we're otherwise cooperating with other instances of + // rustc. + // + // Rather a high number here means that we should be able to keep a lot + // of idle cpus busy. By ensuring that no codegen unit takes *too* long + // to build we'll be guaranteed that all cpus will finish pretty closely + // to one another and we should make relatively optimal use of system + // resources + // + // Note that the main cost of codegen units is that it prevents LLVM + // from inlining across codegen units. Users in general don't have a lot + // of control over how codegen units are split up so it's our job in the + // compiler to ensure that undue performance isn't lost when using + // codegen units (aka we can't require everyone to slap `#[inline]` on + // everything). + // + // If we're compiling at `-O0` then the number doesn't really matter too + // much because performance doesn't matter and inlining is ok to lose. + // In debug mode we just want to try to guarantee that no cpu is stuck + // doing work that could otherwise be farmed to others. + // + // In release mode, however (O1 and above) performance does indeed + // matter! To recover the loss in performance due to inlining we'll be + // enabling ThinLTO by default (the function for which is just below). + // This will ensure that we recover any inlining wins we otherwise lost + // through codegen unit partitioning. + // + // --- + // + // Ok that's a lot of words but the basic tl;dr; is that we want a high + // number here -- but not too high. Additionally we're "safe" to have it + // always at the same number at all optimization levels. + // + // As a result 16 was chosen here! Mostly because it was a power of 2 + // and most benchmarks agreed it was roughly a local optimum. Not very + // scientific. + 16 + } + + pub fn teach(&self, code: &DiagnosticId) -> bool { + self.opts.debugging_opts.teach && self.diagnostic().must_teach(code) + } + + pub fn rust_2015(&self) -> bool { + self.opts.edition == Edition::Edition2015 + } + + /// Are we allowed to use features from the Rust 2018 edition? + pub fn rust_2018(&self) -> bool { + self.opts.edition >= Edition::Edition2018 + } + + pub fn edition(&self) -> Edition { + self.opts.edition + } + + /// Returns `true` if we cannot skip the PLT for shared library calls. + pub fn needs_plt(&self) -> bool { + // Check if the current target usually needs PLT to be enabled. + // The user can use the command line flag to override it. + let needs_plt = self.target.target.options.needs_plt; + + let dbg_opts = &self.opts.debugging_opts; + + let relro_level = dbg_opts.relro_level + .unwrap_or(self.target.target.options.relro_level); + + // Only enable this optimization by default if full relro is also enabled. + // In this case, lazy binding was already unavailable, so nothing is lost. + // This also ensures `-Wl,-z,now` is supported by the linker. + let full_relro = RelroLevel::Full == relro_level; + + // If user didn't explicitly forced us to use / skip the PLT, + // then try to skip it where possible. + dbg_opts.plt.unwrap_or(needs_plt || !full_relro) + } +} + +pub fn build_session( + sopts: config::Options, + local_crate_source_file: Option, + registry: rustc_errors::registry::Registry, +) -> Session { + let file_path_mapping = sopts.file_path_mapping(); + + build_session_with_source_map( + sopts, + local_crate_source_file, + registry, + Lrc::new(source_map::SourceMap::new(file_path_mapping)), + DiagnosticOutput::Default, + Default::default(), + ) +} + +fn default_emitter( + sopts: &config::Options, + registry: rustc_errors::registry::Registry, + source_map: &Lrc, + emitter_dest: Option>, +) -> Box { + let external_macro_backtrace = sopts.debugging_opts.external_macro_backtrace; + match (sopts.error_format, emitter_dest) { + (config::ErrorOutputType::HumanReadable(kind), dst) => { + let (short, color_config) = kind.unzip(); + + if let HumanReadableErrorType::AnnotateSnippet(_) = kind { + let emitter = AnnotateSnippetEmitterWriter::new( + Some(source_map.clone()), + short, + external_macro_backtrace, + ); + Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) + } else { + let emitter = match dst { + None => EmitterWriter::stderr( + color_config, + Some(source_map.clone()), + short, + sopts.debugging_opts.teach, + sopts.debugging_opts.terminal_width, + external_macro_backtrace, + ), + Some(dst) => EmitterWriter::new( + dst, + Some(source_map.clone()), + short, + false, // no teach messages when writing to a buffer + false, // no colors when writing to a buffer + None, // no terminal width + external_macro_backtrace, + ), + }; + Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) + } + }, + (config::ErrorOutputType::Json { pretty, json_rendered }, None) => Box::new( + JsonEmitter::stderr( + Some(registry), + source_map.clone(), + pretty, + json_rendered, + external_macro_backtrace, + ).ui_testing(sopts.debugging_opts.ui_testing), + ), + (config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new( + JsonEmitter::new( + dst, + Some(registry), + source_map.clone(), + pretty, + json_rendered, + external_macro_backtrace, + ).ui_testing(sopts.debugging_opts.ui_testing), + ), + } +} + +pub enum DiagnosticOutput { + Default, + Raw(Box) +} + +pub fn build_session_with_source_map( + sopts: config::Options, + local_crate_source_file: Option, + registry: rustc_errors::registry::Registry, + source_map: Lrc, + diagnostics_output: DiagnosticOutput, + lint_caps: FxHashMap, +) -> Session { + // FIXME: This is not general enough to make the warning lint completely override + // normal diagnostic warnings, since the warning lint can also be denied and changed + // later via the source code. + let warnings_allow = sopts + .lint_opts + .iter() + .filter(|&&(ref key, _)| *key == "warnings") + .map(|&(_, ref level)| *level == lint::Allow) + .last() + .unwrap_or(false); + let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow); + + let can_emit_warnings = !(warnings_allow || cap_lints_allow); + + let treat_err_as_bug = sopts.debugging_opts.treat_err_as_bug; + let dont_buffer_diagnostics = sopts.debugging_opts.dont_buffer_diagnostics; + let report_delayed_bugs = sopts.debugging_opts.report_delayed_bugs; + + let external_macro_backtrace = sopts.debugging_opts.external_macro_backtrace; + + let write_dest = match diagnostics_output { + DiagnosticOutput::Default => None, + DiagnosticOutput::Raw(write) => Some(write), + }; + let emitter = default_emitter(&sopts, registry, &source_map, write_dest); + + let diagnostic_handler = rustc_errors::Handler::with_emitter_and_flags( + emitter, + rustc_errors::HandlerFlags { + can_emit_warnings, + treat_err_as_bug, + report_delayed_bugs, + dont_buffer_diagnostics, + external_macro_backtrace, + ..Default::default() + }, + ); + + build_session_( + sopts, + local_crate_source_file, + diagnostic_handler, + source_map, + lint_caps, + ) +} + +fn build_session_( + sopts: config::Options, + local_crate_source_file: Option, + span_diagnostic: rustc_errors::Handler, + source_map: Lrc, + driver_lint_caps: FxHashMap, +) -> Session { + let self_profiler = + if let SwitchWithOptPath::Enabled(ref d) = sopts.debugging_opts.self_profile { + let directory = if let Some(ref directory) = d { + directory + } else { + std::path::Path::new(".") + }; + + let profiler = SelfProfiler::new( + directory, + sopts.crate_name.as_ref().map(|s| &s[..]), + &sopts.debugging_opts.self_profile_events + ); + match profiler { + Ok(profiler) => { + Some(Arc::new(profiler)) + }, + Err(e) => { + early_warn(sopts.error_format, &format!("failed to create profiler: {}", e)); + None + } + } + } + else { None }; + + let host_triple = TargetTriple::from_triple(config::host_triple()); + let host = Target::search(&host_triple).unwrap_or_else(|e| + span_diagnostic + .fatal(&format!("Error loading host specification: {}", e)) + .raise() + ); + let target_cfg = config::build_target_config(&sopts, &span_diagnostic); + + let parse_sess = ParseSess::with_span_handler( + span_diagnostic, + source_map, + ); + let sysroot = match &sopts.maybe_sysroot { + Some(sysroot) => sysroot.clone(), + None => filesearch::get_or_default_sysroot(), + }; + + let host_triple = config::host_triple(); + let target_triple = sopts.target_triple.triple(); + let host_tlib_path = SearchPath::from_sysroot_and_triple(&sysroot, host_triple); + let target_tlib_path = if host_triple == target_triple { + None + } else { + Some(SearchPath::from_sysroot_and_triple(&sysroot, target_triple)) + }; + + let file_path_mapping = sopts.file_path_mapping(); + + let local_crate_source_file = + local_crate_source_file.map(|path| file_path_mapping.map_prefix(path).0); + + let optimization_fuel_crate = sopts.debugging_opts.fuel.as_ref().map(|i| i.0.clone()); + let optimization_fuel = Lock::new(OptimizationFuel { + remaining: sopts.debugging_opts.fuel.as_ref().map(|i| i.1).unwrap_or(0), + out_of_fuel: false, + }); + let print_fuel_crate = sopts.debugging_opts.print_fuel.clone(); + let print_fuel = AtomicU64::new(0); + + let working_dir = env::current_dir().unwrap_or_else(|e| + parse_sess.span_diagnostic + .fatal(&format!("Current directory is invalid: {}", e)) + .raise() + ); + let working_dir = file_path_mapping.map_prefix(working_dir); + + let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph { + CguReuseTracker::new() + } else { + CguReuseTracker::new_disabled() + }; + + let sess = Session { + target: target_cfg, + host, + opts: sopts, + host_tlib_path, + target_tlib_path, + parse_sess, + sysroot, + local_crate_source_file, + working_dir, + one_time_diagnostics: Default::default(), + crate_types: Once::new(), + crate_disambiguator: Once::new(), + features: Once::new(), + recursion_limit: Once::new(), + type_length_limit: Once::new(), + imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())), + incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), + cgu_reuse_tracker, + prof: SelfProfilerRef::new(self_profiler), + perf_stats: PerfStats { + symbol_hash_time: Lock::new(Duration::from_secs(0)), + decode_def_path_tables_time: Lock::new(Duration::from_secs(0)), + queries_canonicalized: AtomicUsize::new(0), + normalize_ty_after_erasing_regions: AtomicUsize::new(0), + normalize_projection_ty: AtomicUsize::new(0), + }, + code_stats: Default::default(), + optimization_fuel_crate, + optimization_fuel, + print_fuel_crate, + print_fuel, + jobserver: jobserver::client(), + driver_lint_caps, + trait_methods_not_found: Lock::new(Default::default()), + confused_type_with_std_module: Lock::new(Default::default()), + }; + + validate_commandline_args_with_session_available(&sess); + + sess +} + +// If it is useful to have a Session available already for validating a +// commandline argument, you can do so here. +fn validate_commandline_args_with_session_available(sess: &Session) { + // Since we don't know if code in an rlib will be linked to statically or + // dynamically downstream, rustc generates `__imp_` symbols that help the + // MSVC linker deal with this lack of knowledge (#27438). Unfortunately, + // these manually generated symbols confuse LLD when it tries to merge + // bitcode during ThinLTO. Therefore we disallow dynamic linking on MSVC + // when compiling for LLD ThinLTO. This way we can validly just not generate + // the `dllimport` attributes and `__imp_` symbols in that case. + if sess.opts.cg.linker_plugin_lto.enabled() && + sess.opts.cg.prefer_dynamic && + sess.target.target.options.is_like_msvc { + sess.err("Linker plugin based LTO is not supported together with \ + `-C prefer-dynamic` when targeting MSVC"); + } + + // Make sure that any given profiling data actually exists so LLVM can't + // decide to silently skip PGO. + if let Some(ref path) = sess.opts.cg.profile_use { + if !path.exists() { + sess.err(&format!("File `{}` passed to `-C profile-use` does not exist.", + path.display())); + } + } + + // PGO does not work reliably with panic=unwind on Windows. Let's make it + // an error to combine the two for now. It always runs into an assertions + // if LLVM is built with assertions, but without assertions it sometimes + // does not crash and will probably generate a corrupted binary. + // We should only display this error if we're actually going to run PGO. + // If we're just supposed to print out some data, don't show the error (#61002). + if sess.opts.cg.profile_generate.enabled() && + sess.target.target.options.is_like_msvc && + sess.panic_strategy() == PanicStrategy::Unwind && + sess.opts.prints.iter().all(|&p| p == PrintRequest::NativeStaticLibs) { + sess.err("Profile-guided optimization does not yet work in conjunction \ + with `-Cpanic=unwind` on Windows when targeting MSVC. \ + See https://github.com/rust-lang/rust/issues/61002 for details."); + } +} + +/// Hash value constructed out of all the `-C metadata` arguments passed to the +/// compiler. Together with the crate-name forms a unique global identifier for +/// the crate. +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, RustcEncodable, RustcDecodable)] +pub struct CrateDisambiguator(Fingerprint); + +impl CrateDisambiguator { + pub fn to_fingerprint(self) -> Fingerprint { + self.0 + } +} + +impl fmt::Display for CrateDisambiguator { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let (a, b) = self.0.as_value(); + let as_u128 = a as u128 | ((b as u128) << 64); + f.write_str(&base_n::encode(as_u128, base_n::CASE_INSENSITIVE)) + } +} + +impl From for CrateDisambiguator { + fn from(fingerprint: Fingerprint) -> CrateDisambiguator { + CrateDisambiguator(fingerprint) + } +} + +impl_stable_hash_via_hash!(CrateDisambiguator); + +/// Holds data on the current incremental compilation session, if there is one. +#[derive(Debug)] +pub enum IncrCompSession { + /// This is the state the session will be in until the incr. comp. dir is + /// needed. + NotInitialized, + /// This is the state during which the session directory is private and can + /// be modified. + Active { + session_directory: PathBuf, + lock_file: flock::Lock, + load_dep_graph: bool, + }, + /// This is the state after the session directory has been finalized. In this + /// state, the contents of the directory must not be modified any more. + Finalized { session_directory: PathBuf }, + /// This is an error state that is reached when some compilation error has + /// occurred. It indicates that the contents of the session directory must + /// not be used, since they might be invalid. + InvalidBecauseOfErrors { session_directory: PathBuf }, +} + +pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { + let emitter: Box = match output { + config::ErrorOutputType::HumanReadable(kind) => { + let (short, color_config) = kind.unzip(); + Box::new(EmitterWriter::stderr(color_config, None, short, false, None, false)) + } + config::ErrorOutputType::Json { pretty, json_rendered } => + Box::new(JsonEmitter::basic(pretty, json_rendered, false)), + }; + let handler = rustc_errors::Handler::with_emitter(true, None, emitter); + handler.struct_fatal(msg).emit(); + rustc_errors::FatalError.raise(); +} + +pub fn early_warn(output: config::ErrorOutputType, msg: &str) { + let emitter: Box = match output { + config::ErrorOutputType::HumanReadable(kind) => { + let (short, color_config) = kind.unzip(); + Box::new(EmitterWriter::stderr(color_config, None, short, false, None, false)) + } + config::ErrorOutputType::Json { pretty, json_rendered } => + Box::new(JsonEmitter::basic(pretty, json_rendered, false)), + }; + let handler = rustc_errors::Handler::with_emitter(true, None, emitter); + handler.struct_warn(msg).emit(); +} + +pub type CompileResult = Result<(), ErrorReported>; -- cgit 1.4.1-3-g733a5