|  | //! Contains infrastructure for configuring the compiler, including parsing | 
|  | //! command-line options. | 
|  |  | 
|  | #![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable | 
|  |  | 
|  | use std::collections::btree_map::{ | 
|  | Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, | 
|  | }; | 
|  | use std::collections::{BTreeMap, BTreeSet}; | 
|  | use std::ffi::OsStr; | 
|  | use std::hash::Hash; | 
|  | use std::path::{Path, PathBuf}; | 
|  | use std::str::{self, FromStr}; | 
|  | use std::sync::LazyLock; | 
|  | use std::{cmp, fmt, fs, iter}; | 
|  |  | 
|  | use externs::{ExternOpt, split_extern_opt}; | 
|  | use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; | 
|  | use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey}; | 
|  | use rustc_errors::emitter::HumanReadableErrorType; | 
|  | use rustc_errors::{ColorConfig, DiagArgValue, DiagCtxtFlags, IntoDiagArg}; | 
|  | use rustc_feature::UnstableFeatures; | 
|  | use rustc_macros::{Decodable, Encodable, HashStable_Generic}; | 
|  | use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION}; | 
|  | use rustc_span::source_map::FilePathMapping; | 
|  | use rustc_span::{ | 
|  | FileName, FileNameDisplayPreference, FileNameEmbeddablePreference, RealFileName, | 
|  | SourceFileHashAlgorithm, Symbol, sym, | 
|  | }; | 
|  | use rustc_target::spec::{ | 
|  | FramePointer, LinkSelfContainedComponents, LinkerFeatures, SplitDebuginfo, Target, TargetTuple, | 
|  | }; | 
|  | use tracing::debug; | 
|  |  | 
|  | pub use crate::config::cfg::{Cfg, CheckCfg, ExpectedValues}; | 
|  | use crate::config::native_libs::parse_native_libs; | 
|  | use crate::errors::FileWriteFail; | 
|  | pub use crate::options::*; | 
|  | use crate::search_paths::SearchPath; | 
|  | use crate::utils::CanonicalizedPath; | 
|  | use crate::{EarlyDiagCtxt, HashStableContext, Session, filesearch, lint}; | 
|  |  | 
|  | mod cfg; | 
|  | mod externs; | 
|  | mod native_libs; | 
|  | pub mod sigpipe; | 
|  |  | 
|  | pub const PRINT_KINDS: &[(&str, PrintKind)] = &[ | 
|  | // tidy-alphabetical-start | 
|  | ("all-target-specs-json", PrintKind::AllTargetSpecsJson), | 
|  | ("calling-conventions", PrintKind::CallingConventions), | 
|  | ("cfg", PrintKind::Cfg), | 
|  | ("check-cfg", PrintKind::CheckCfg), | 
|  | ("code-models", PrintKind::CodeModels), | 
|  | ("crate-name", PrintKind::CrateName), | 
|  | ("crate-root-lint-levels", PrintKind::CrateRootLintLevels), | 
|  | ("deployment-target", PrintKind::DeploymentTarget), | 
|  | ("file-names", PrintKind::FileNames), | 
|  | ("host-tuple", PrintKind::HostTuple), | 
|  | ("link-args", PrintKind::LinkArgs), | 
|  | ("native-static-libs", PrintKind::NativeStaticLibs), | 
|  | ("relocation-models", PrintKind::RelocationModels), | 
|  | ("split-debuginfo", PrintKind::SplitDebuginfo), | 
|  | ("stack-protector-strategies", PrintKind::StackProtectorStrategies), | 
|  | ("supported-crate-types", PrintKind::SupportedCrateTypes), | 
|  | ("sysroot", PrintKind::Sysroot), | 
|  | ("target-cpus", PrintKind::TargetCPUs), | 
|  | ("target-features", PrintKind::TargetFeatures), | 
|  | ("target-libdir", PrintKind::TargetLibdir), | 
|  | ("target-list", PrintKind::TargetList), | 
|  | ("target-spec-json", PrintKind::TargetSpecJson), | 
|  | ("tls-models", PrintKind::TlsModels), | 
|  | // tidy-alphabetical-end | 
|  | ]; | 
|  |  | 
|  | /// The different settings that the `-C strip` flag can have. | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | 
|  | pub enum Strip { | 
|  | /// Do not strip at all. | 
|  | None, | 
|  |  | 
|  | /// Strip debuginfo. | 
|  | Debuginfo, | 
|  |  | 
|  | /// Strip all symbols. | 
|  | Symbols, | 
|  | } | 
|  |  | 
|  | /// The different settings that the `-C control-flow-guard` flag can have. | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | 
|  | pub enum CFGuard { | 
|  | /// Do not emit Control Flow Guard metadata or checks. | 
|  | Disabled, | 
|  |  | 
|  | /// Emit Control Flow Guard metadata but no checks. | 
|  | NoChecks, | 
|  |  | 
|  | /// Emit Control Flow Guard metadata and checks. | 
|  | Checks, | 
|  | } | 
|  |  | 
|  | /// The different settings that the `-Z cf-protection` flag can have. | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | 
|  | pub enum CFProtection { | 
|  | /// Do not enable control-flow protection | 
|  | None, | 
|  |  | 
|  | /// Emit control-flow protection for branches (enables indirect branch tracking). | 
|  | Branch, | 
|  |  | 
|  | /// Emit control-flow protection for returns. | 
|  | Return, | 
|  |  | 
|  | /// Emit control-flow protection for both branches and returns. | 
|  | Full, | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Copy, Debug, PartialEq, Hash, HashStable_Generic)] | 
|  | pub enum OptLevel { | 
|  | /// `-Copt-level=0` | 
|  | No, | 
|  | /// `-Copt-level=1` | 
|  | Less, | 
|  | /// `-Copt-level=2` | 
|  | More, | 
|  | /// `-Copt-level=3` / `-O` | 
|  | Aggressive, | 
|  | /// `-Copt-level=s` | 
|  | Size, | 
|  | /// `-Copt-level=z` | 
|  | SizeMin, | 
|  | } | 
|  |  | 
|  | /// This is what the `LtoCli` values get mapped to after resolving defaults and | 
|  | /// and taking other command line options into account. | 
|  | /// | 
|  | /// Note that linker plugin-based LTO is a different mechanism entirely. | 
|  | #[derive(Clone, PartialEq)] | 
|  | pub enum Lto { | 
|  | /// Don't do any LTO whatsoever. | 
|  | No, | 
|  |  | 
|  | /// Do a full-crate-graph (inter-crate) LTO with ThinLTO. | 
|  | Thin, | 
|  |  | 
|  | /// Do a local ThinLTO (intra-crate, over the CodeGen Units of the local crate only). This is | 
|  | /// only relevant if multiple CGUs are used. | 
|  | ThinLocal, | 
|  |  | 
|  | /// Do a full-crate-graph (inter-crate) 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, | 
|  | } | 
|  |  | 
|  | /// The different settings that the `-C instrument-coverage` flag can have. | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | 
|  | pub enum InstrumentCoverage { | 
|  | /// `-C instrument-coverage=no` (or `off`, `false` etc.) | 
|  | No, | 
|  | /// `-C instrument-coverage` or `-C instrument-coverage=yes` | 
|  | Yes, | 
|  | } | 
|  |  | 
|  | /// Individual flag values controlled by `-Zcoverage-options`. | 
|  | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] | 
|  | pub struct CoverageOptions { | 
|  | pub level: CoverageLevel, | 
|  |  | 
|  | /// `-Zcoverage-options=no-mir-spans`: Don't extract block coverage spans | 
|  | /// from MIR statements/terminators, making it easier to inspect/debug | 
|  | /// branch and MC/DC coverage mappings. | 
|  | /// | 
|  | /// For internal debugging only. If other code changes would make it hard | 
|  | /// to keep supporting this flag, remove it. | 
|  | pub no_mir_spans: bool, | 
|  |  | 
|  | /// `-Zcoverage-options=discard-all-spans-in-codegen`: During codegen, | 
|  | /// discard all coverage spans as though they were invalid. Needed by | 
|  | /// regression tests for #133606, because we don't have an easy way to | 
|  | /// reproduce it from actual source code. | 
|  | pub discard_all_spans_in_codegen: bool, | 
|  | } | 
|  |  | 
|  | /// Controls whether branch coverage or MC/DC coverage is enabled. | 
|  | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] | 
|  | pub enum CoverageLevel { | 
|  | /// Instrument for coverage at the MIR block level. | 
|  | #[default] | 
|  | Block, | 
|  | /// Also instrument branch points (includes block coverage). | 
|  | Branch, | 
|  | /// Same as branch coverage, but also adds branch instrumentation for | 
|  | /// certain boolean expressions that are not directly used for branching. | 
|  | /// | 
|  | /// For example, in the following code, `b` does not directly participate | 
|  | /// in a branch, but condition coverage will instrument it as its own | 
|  | /// artificial branch: | 
|  | /// ``` | 
|  | /// # let (a, b) = (false, true); | 
|  | /// let x = a && b; | 
|  | /// //           ^ last operand | 
|  | /// ``` | 
|  | /// | 
|  | /// This level is mainly intended to be a stepping-stone towards full MC/DC | 
|  | /// instrumentation, so it might be removed in the future when MC/DC is | 
|  | /// sufficiently complete, or if it is making MC/DC changes difficult. | 
|  | Condition, | 
|  | /// Instrument for MC/DC. Mostly a superset of condition coverage, but might | 
|  | /// differ in some corner cases. | 
|  | Mcdc, | 
|  | } | 
|  |  | 
|  | // The different settings that the `-Z offload` flag can have. | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | 
|  | pub enum Offload { | 
|  | /// Enable the llvm offload pipeline | 
|  | Enable, | 
|  | } | 
|  |  | 
|  | /// The different settings that the `-Z autodiff` flag can have. | 
|  | #[derive(Clone, PartialEq, Hash, Debug)] | 
|  | pub enum AutoDiff { | 
|  | /// Enable the autodiff opt pipeline | 
|  | Enable, | 
|  |  | 
|  | /// Print TypeAnalysis information | 
|  | PrintTA, | 
|  | /// Print TypeAnalysis information for a specific function | 
|  | PrintTAFn(String), | 
|  | /// Print ActivityAnalysis Information | 
|  | PrintAA, | 
|  | /// Print Performance Warnings from Enzyme | 
|  | PrintPerf, | 
|  | /// Print intermediate IR generation steps | 
|  | PrintSteps, | 
|  | /// Print the module, before running autodiff. | 
|  | PrintModBefore, | 
|  | /// Print the module after running autodiff. | 
|  | PrintModAfter, | 
|  | /// Print the module after running autodiff and optimizations. | 
|  | PrintModFinal, | 
|  |  | 
|  | /// Print all passes scheduled by LLVM | 
|  | PrintPasses, | 
|  | /// Disable extra opt run after running autodiff | 
|  | NoPostopt, | 
|  | /// Enzyme's loose type debug helper (can cause incorrect gradients!!) | 
|  | /// Usable in cases where Enzyme errors with `can not deduce type of X`. | 
|  | LooseTypes, | 
|  | /// Runs Enzyme's aggressive inlining | 
|  | Inline, | 
|  | } | 
|  |  | 
|  | /// Settings for `-Z instrument-xray` flag. | 
|  | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] | 
|  | pub struct InstrumentXRay { | 
|  | /// `-Z instrument-xray=always`, force instrumentation | 
|  | pub always: bool, | 
|  | /// `-Z instrument-xray=never`, disable instrumentation | 
|  | pub never: bool, | 
|  | /// `-Z instrument-xray=ignore-loops`, ignore presence of loops, | 
|  | /// instrument functions based only on instruction count | 
|  | pub ignore_loops: bool, | 
|  | /// `-Z instrument-xray=instruction-threshold=N`, explicitly set instruction threshold | 
|  | /// for instrumentation, or `None` to use compiler's default | 
|  | pub instruction_threshold: Option<usize>, | 
|  | /// `-Z instrument-xray=skip-entry`, do not instrument function entry | 
|  | pub skip_entry: bool, | 
|  | /// `-Z instrument-xray=skip-exit`, do not instrument function exit | 
|  | pub skip_exit: bool, | 
|  | } | 
|  |  | 
|  | #[derive(Clone, PartialEq, Hash, Debug)] | 
|  | pub enum LinkerPluginLto { | 
|  | LinkerPlugin(PathBuf), | 
|  | LinkerPluginAuto, | 
|  | Disabled, | 
|  | } | 
|  |  | 
|  | impl LinkerPluginLto { | 
|  | pub fn enabled(&self) -> bool { | 
|  | match *self { | 
|  | LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true, | 
|  | LinkerPluginLto::Disabled => false, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// The different values `-C link-self-contained` can take: a list of individually enabled or | 
|  | /// disabled components used during linking, coming from the rustc distribution, instead of being | 
|  | /// found somewhere on the host system. | 
|  | /// | 
|  | /// They can be set in bulk via `-C link-self-contained=yes|y|on` or `-C | 
|  | /// link-self-contained=no|n|off`, and those boolean values are the historical defaults. | 
|  | /// | 
|  | /// But each component is fine-grained, and can be unstably targeted, to use: | 
|  | /// - some CRT objects | 
|  | /// - the libc static library | 
|  | /// - libgcc/libunwind libraries | 
|  | /// - a linker we distribute | 
|  | /// - some sanitizer runtime libraries | 
|  | /// - all other MinGW libraries and Windows import libs | 
|  | /// | 
|  | #[derive(Default, Clone, PartialEq, Debug)] | 
|  | pub struct LinkSelfContained { | 
|  | /// Whether the user explicitly set `-C link-self-contained` on or off, the historical values. | 
|  | /// Used for compatibility with the existing opt-in and target inference. | 
|  | pub explicitly_set: Option<bool>, | 
|  |  | 
|  | /// The components that are enabled on the CLI, using the `+component` syntax or one of the | 
|  | /// `true` shortcuts. | 
|  | enabled_components: LinkSelfContainedComponents, | 
|  |  | 
|  | /// The components that are disabled on the CLI, using the `-component` syntax or one of the | 
|  | /// `false` shortcuts. | 
|  | disabled_components: LinkSelfContainedComponents, | 
|  | } | 
|  |  | 
|  | impl LinkSelfContained { | 
|  | /// Incorporates an enabled or disabled component as specified on the CLI, if possible. | 
|  | /// For example: `+linker`, and `-crto`. | 
|  | pub(crate) fn handle_cli_component(&mut self, component: &str) -> Option<()> { | 
|  | // Note that for example `-Cself-contained=y -Cself-contained=-linker` is not an explicit | 
|  | // set of all values like `y` or `n` used to be. Therefore, if this flag had previously been | 
|  | // set in bulk with its historical values, then manually setting a component clears that | 
|  | // `explicitly_set` state. | 
|  | if let Some(component_to_enable) = component.strip_prefix('+') { | 
|  | self.explicitly_set = None; | 
|  | self.enabled_components | 
|  | .insert(LinkSelfContainedComponents::from_str(component_to_enable)?); | 
|  | Some(()) | 
|  | } else if let Some(component_to_disable) = component.strip_prefix('-') { | 
|  | self.explicitly_set = None; | 
|  | self.disabled_components | 
|  | .insert(LinkSelfContainedComponents::from_str(component_to_disable)?); | 
|  | Some(()) | 
|  | } else { | 
|  | None | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Turns all components on or off and records that this was done explicitly for compatibility | 
|  | /// purposes. | 
|  | pub(crate) fn set_all_explicitly(&mut self, enabled: bool) { | 
|  | self.explicitly_set = Some(enabled); | 
|  |  | 
|  | if enabled { | 
|  | self.enabled_components = LinkSelfContainedComponents::all(); | 
|  | self.disabled_components = LinkSelfContainedComponents::empty(); | 
|  | } else { | 
|  | self.enabled_components = LinkSelfContainedComponents::empty(); | 
|  | self.disabled_components = LinkSelfContainedComponents::all(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Helper creating a fully enabled `LinkSelfContained` instance. Used in tests. | 
|  | pub fn on() -> Self { | 
|  | let mut on = LinkSelfContained::default(); | 
|  | on.set_all_explicitly(true); | 
|  | on | 
|  | } | 
|  |  | 
|  | /// To help checking CLI usage while some of the values are unstable: returns whether one of the | 
|  | /// unstable components was set individually, for the given `TargetTuple`. This would also | 
|  | /// require the `-Zunstable-options` flag, to be allowed. | 
|  | fn check_unstable_variants(&self, target_tuple: &TargetTuple) -> Result<(), String> { | 
|  | if self.explicitly_set.is_some() { | 
|  | return Ok(()); | 
|  | } | 
|  |  | 
|  | // `-C link-self-contained=-linker` is only stable on x64 linux. | 
|  | let has_minus_linker = self.disabled_components.is_linker_enabled(); | 
|  | if has_minus_linker && target_tuple.tuple() != "x86_64-unknown-linux-gnu" { | 
|  | return Err(format!( | 
|  | "`-C link-self-contained=-linker` is unstable on the `{target_tuple}` \ | 
|  | target. The `-Z unstable-options` flag must also be passed to use it on this target", | 
|  | )); | 
|  | } | 
|  |  | 
|  | // Any `+linker` or other component used is unstable, and that's an error. | 
|  | let unstable_enabled = self.enabled_components; | 
|  | let unstable_disabled = self.disabled_components - LinkSelfContainedComponents::LINKER; | 
|  | if !unstable_enabled.union(unstable_disabled).is_empty() { | 
|  | return Err(String::from( | 
|  | "only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` \ | 
|  | are stable, the `-Z unstable-options` flag must also be passed to use \ | 
|  | the unstable values", | 
|  | )); | 
|  | } | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | /// Returns whether the self-contained linker component was enabled on the CLI, using the | 
|  | /// `-C link-self-contained=+linker` syntax, or one of the `true` shortcuts. | 
|  | pub fn is_linker_enabled(&self) -> bool { | 
|  | self.enabled_components.contains(LinkSelfContainedComponents::LINKER) | 
|  | } | 
|  |  | 
|  | /// Returns whether the self-contained linker component was disabled on the CLI, using the | 
|  | /// `-C link-self-contained=-linker` syntax, or one of the `false` shortcuts. | 
|  | pub fn is_linker_disabled(&self) -> bool { | 
|  | self.disabled_components.contains(LinkSelfContainedComponents::LINKER) | 
|  | } | 
|  |  | 
|  | /// Returns CLI inconsistencies to emit errors: individual components were both enabled and | 
|  | /// disabled. | 
|  | fn check_consistency(&self) -> Option<LinkSelfContainedComponents> { | 
|  | if self.explicitly_set.is_some() { | 
|  | None | 
|  | } else { | 
|  | let common = self.enabled_components.intersection(self.disabled_components); | 
|  | if common.is_empty() { None } else { Some(common) } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// The different values that `-C linker-features` can take on the CLI: a list of individually | 
|  | /// enabled or disabled features used during linking. | 
|  | /// | 
|  | /// There is no need to enable or disable them in bulk. Each feature is fine-grained, and can be | 
|  | /// used to turn `LinkerFeatures` on or off, without needing to change the linker flavor: | 
|  | /// - using the system lld, or the self-contained `rust-lld` linker | 
|  | /// - using a C/C++ compiler to drive the linker (not yet exposed on the CLI) | 
|  | /// - etc. | 
|  | #[derive(Default, Copy, Clone, PartialEq, Debug)] | 
|  | pub struct LinkerFeaturesCli { | 
|  | /// The linker features that are enabled on the CLI, using the `+feature` syntax. | 
|  | pub enabled: LinkerFeatures, | 
|  |  | 
|  | /// The linker features that are disabled on the CLI, using the `-feature` syntax. | 
|  | pub disabled: LinkerFeatures, | 
|  | } | 
|  |  | 
|  | impl LinkerFeaturesCli { | 
|  | /// Accumulates an enabled or disabled feature as specified on the CLI, if possible. | 
|  | /// For example: `+lld`, and `-lld`. | 
|  | pub(crate) fn handle_cli_feature(&mut self, feature: &str) -> Option<()> { | 
|  | // Duplicate flags are reduced as we go, the last occurrence wins: | 
|  | // `+feature,-feature,+feature` only enables the feature, and does not record it as both | 
|  | // enabled and disabled on the CLI. | 
|  | // We also only expose `+/-lld` at the moment, as it's currently the only implemented linker | 
|  | // feature and toggling `LinkerFeatures::CC` would be a noop. | 
|  | match feature { | 
|  | "+lld" => { | 
|  | self.enabled.insert(LinkerFeatures::LLD); | 
|  | self.disabled.remove(LinkerFeatures::LLD); | 
|  | Some(()) | 
|  | } | 
|  | "-lld" => { | 
|  | self.disabled.insert(LinkerFeatures::LLD); | 
|  | self.enabled.remove(LinkerFeatures::LLD); | 
|  | Some(()) | 
|  | } | 
|  | _ => None, | 
|  | } | 
|  | } | 
|  |  | 
|  | /// When *not* using `-Z unstable-options` on the CLI, ensure only stable linker features are | 
|  | /// used, for the given `TargetTuple`. Returns `Ok` if no unstable variants are used. | 
|  | /// The caller should ensure that e.g. `nightly_options::is_unstable_enabled()` | 
|  | /// returns false. | 
|  | pub(crate) fn check_unstable_variants(&self, target_tuple: &TargetTuple) -> Result<(), String> { | 
|  | // `-C linker-features=-lld` is only stable on x64 linux. | 
|  | let has_minus_lld = self.disabled.is_lld_enabled(); | 
|  | if has_minus_lld && target_tuple.tuple() != "x86_64-unknown-linux-gnu" { | 
|  | return Err(format!( | 
|  | "`-C linker-features=-lld` is unstable on the `{target_tuple}` \ | 
|  | target. The `-Z unstable-options` flag must also be passed to use it on this target", | 
|  | )); | 
|  | } | 
|  |  | 
|  | // Any `+lld` or non-lld feature used is unstable, and that's an error. | 
|  | let unstable_enabled = self.enabled; | 
|  | let unstable_disabled = self.disabled - LinkerFeatures::LLD; | 
|  | if !unstable_enabled.union(unstable_disabled).is_empty() { | 
|  | let unstable_features: Vec<_> = unstable_enabled | 
|  | .iter() | 
|  | .map(|f| format!("+{}", f.as_str().unwrap())) | 
|  | .chain(unstable_disabled.iter().map(|f| format!("-{}", f.as_str().unwrap()))) | 
|  | .collect(); | 
|  | return Err(format!( | 
|  | "`-C linker-features={}` is unstable, and also requires the \ | 
|  | `-Z unstable-options` flag to be used", | 
|  | unstable_features.join(","), | 
|  | )); | 
|  | } | 
|  |  | 
|  | Ok(()) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Used with `-Z assert-incr-state`. | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | 
|  | pub enum IncrementalStateAssertion { | 
|  | /// Found and loaded an existing session directory. | 
|  | /// | 
|  | /// Note that this says nothing about whether any particular query | 
|  | /// will be found to be red or green. | 
|  | Loaded, | 
|  | /// Did not load an existing session directory. | 
|  | NotLoaded, | 
|  | } | 
|  |  | 
|  | /// The different settings that can be enabled via the `-Z location-detail` flag. | 
|  | #[derive(Copy, Clone, PartialEq, Hash, Debug)] | 
|  | pub struct LocationDetail { | 
|  | pub file: bool, | 
|  | pub line: bool, | 
|  | pub column: bool, | 
|  | } | 
|  |  | 
|  | impl LocationDetail { | 
|  | pub(crate) fn all() -> Self { | 
|  | Self { file: true, line: true, column: true } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Values for the `-Z fmt-debug` flag. | 
|  | #[derive(Copy, Clone, PartialEq, Hash, Debug)] | 
|  | pub enum FmtDebug { | 
|  | /// Derive fully-featured implementation | 
|  | Full, | 
|  | /// Print only type name, without fields | 
|  | Shallow, | 
|  | /// `#[derive(Debug)]` and `{:?}` are no-ops | 
|  | None, | 
|  | } | 
|  |  | 
|  | impl FmtDebug { | 
|  | pub(crate) fn all() -> [Symbol; 3] { | 
|  | [sym::full, sym::none, sym::shallow] | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, PartialEq, Hash, Debug)] | 
|  | pub enum SwitchWithOptPath { | 
|  | Enabled(Option<PathBuf>), | 
|  | 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, HashStable_Generic)] | 
|  | #[derive(Encodable, Decodable)] | 
|  | pub enum SymbolManglingVersion { | 
|  | Legacy, | 
|  | V0, | 
|  | Hashed, | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Copy, Debug, PartialEq, Hash)] | 
|  | pub enum DebugInfo { | 
|  | None, | 
|  | LineDirectivesOnly, | 
|  | LineTablesOnly, | 
|  | Limited, | 
|  | Full, | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Copy, Debug, PartialEq, Hash)] | 
|  | pub enum DebugInfoCompression { | 
|  | None, | 
|  | Zlib, | 
|  | Zstd, | 
|  | } | 
|  |  | 
|  | impl ToString for DebugInfoCompression { | 
|  | fn to_string(&self) -> String { | 
|  | match self { | 
|  | DebugInfoCompression::None => "none", | 
|  | DebugInfoCompression::Zlib => "zlib", | 
|  | DebugInfoCompression::Zstd => "zstd", | 
|  | } | 
|  | .to_owned() | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Copy, Debug, PartialEq, Hash)] | 
|  | pub enum MirStripDebugInfo { | 
|  | None, | 
|  | LocalsInTinyFunctions, | 
|  | AllLocals, | 
|  | } | 
|  |  | 
|  | /// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split | 
|  | /// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform | 
|  | /// uses DWARF for debug-information. | 
|  | /// | 
|  | /// Some debug-information requires link-time relocation and some does not. LLVM can partition | 
|  | /// the debuginfo into sections depending on whether or not it requires link-time relocation. Split | 
|  | /// DWARF provides a mechanism which allows the linker to skip the sections which don't require | 
|  | /// link-time relocation - either by putting those sections in DWARF object files, or by keeping | 
|  | /// them in the object file in such a way that the linker will skip them. | 
|  | #[derive(Clone, Copy, Debug, PartialEq, Hash)] | 
|  | pub enum SplitDwarfKind { | 
|  | /// Sections which do not require relocation are written into object file but ignored by the | 
|  | /// linker. | 
|  | Single, | 
|  | /// Sections which do not require relocation are written into a DWARF object (`.dwo`) file | 
|  | /// which is ignored by the linker. | 
|  | Split, | 
|  | } | 
|  |  | 
|  | impl FromStr for SplitDwarfKind { | 
|  | type Err = (); | 
|  |  | 
|  | fn from_str(s: &str) -> Result<Self, ()> { | 
|  | Ok(match s { | 
|  | "single" => SplitDwarfKind::Single, | 
|  | "split" => SplitDwarfKind::Split, | 
|  | _ => return Err(()), | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | macro_rules! define_output_types { | 
|  | ( | 
|  | $( | 
|  | $(#[doc = $doc:expr])* | 
|  | $Variant:ident => { | 
|  | shorthand: $shorthand:expr, | 
|  | extension: $extension:expr, | 
|  | description: $description:expr, | 
|  | default_filename: $default_filename:expr, | 
|  | is_text: $is_text:expr, | 
|  | compatible_with_cgus_and_single_output: $compatible:expr | 
|  | } | 
|  | ),* $(,)? | 
|  | ) => { | 
|  | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)] | 
|  | #[derive(Encodable, Decodable)] | 
|  | pub enum OutputType { | 
|  | $( | 
|  | $(#[doc = $doc])* | 
|  | $Variant, | 
|  | )* | 
|  | } | 
|  |  | 
|  |  | 
|  | impl StableOrd for OutputType { | 
|  | const CAN_USE_UNSTABLE_SORT: bool = true; | 
|  |  | 
|  | // Trivial C-Style enums have a stable sort order across compilation sessions. | 
|  | const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); | 
|  | } | 
|  |  | 
|  | impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType { | 
|  | type KeyType = Self; | 
|  |  | 
|  | fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { | 
|  | *self | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | impl OutputType { | 
|  | pub fn iter_all() -> impl Iterator<Item = OutputType> { | 
|  | static ALL_VARIANTS: &[OutputType] = &[ | 
|  | $( | 
|  | OutputType::$Variant, | 
|  | )* | 
|  | ]; | 
|  | ALL_VARIANTS.iter().copied() | 
|  | } | 
|  |  | 
|  | fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool { | 
|  | match *self { | 
|  | $( | 
|  | OutputType::$Variant => $compatible, | 
|  | )* | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn shorthand(&self) -> &'static str { | 
|  | match *self { | 
|  | $( | 
|  | OutputType::$Variant => $shorthand, | 
|  | )* | 
|  | } | 
|  | } | 
|  |  | 
|  | fn from_shorthand(shorthand: &str) -> Option<Self> { | 
|  | match shorthand { | 
|  | $( | 
|  | s if s == $shorthand => Some(OutputType::$Variant), | 
|  | )* | 
|  | _ => None, | 
|  | } | 
|  | } | 
|  |  | 
|  | fn shorthands_display() -> String { | 
|  | let shorthands = vec![ | 
|  | $( | 
|  | format!("`{}`", $shorthand), | 
|  | )* | 
|  | ]; | 
|  | shorthands.join(", ") | 
|  | } | 
|  |  | 
|  | pub fn extension(&self) -> &'static str { | 
|  | match *self { | 
|  | $( | 
|  | OutputType::$Variant => $extension, | 
|  | )* | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn is_text_output(&self) -> bool { | 
|  | match *self { | 
|  | $( | 
|  | OutputType::$Variant => $is_text, | 
|  | )* | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn description(&self) -> &'static str { | 
|  | match *self { | 
|  | $( | 
|  | OutputType::$Variant => $description, | 
|  | )* | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn default_filename(&self) -> &'static str { | 
|  | match *self { | 
|  | $( | 
|  | OutputType::$Variant => $default_filename, | 
|  | )* | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | define_output_types! { | 
|  | Assembly => { | 
|  | shorthand: "asm", | 
|  | extension: "s", | 
|  | description: "Generates a file with the crate's assembly code", | 
|  | default_filename: "CRATE_NAME.s", | 
|  | is_text: true, | 
|  | compatible_with_cgus_and_single_output: false | 
|  | }, | 
|  | #[doc = "This is the optimized bitcode, which could be either pre-LTO or non-LTO bitcode,"] | 
|  | #[doc = "depending on the specific request type."] | 
|  | Bitcode => { | 
|  | shorthand: "llvm-bc", | 
|  | extension: "bc", | 
|  | description: "Generates a binary file containing the LLVM bitcode", | 
|  | default_filename: "CRATE_NAME.bc", | 
|  | is_text: false, | 
|  | compatible_with_cgus_and_single_output: false | 
|  | }, | 
|  | DepInfo => { | 
|  | shorthand: "dep-info", | 
|  | extension: "d", | 
|  | description: "Generates a file with Makefile syntax that indicates all the source files that were loaded to generate the crate", | 
|  | default_filename: "CRATE_NAME.d", | 
|  | is_text: true, | 
|  | compatible_with_cgus_and_single_output: true | 
|  | }, | 
|  | Exe => { | 
|  | shorthand: "link", | 
|  | extension: "", | 
|  | description: "Generates the crates specified by --crate-type. This is the default if --emit is not specified", | 
|  | default_filename: "(platform and crate-type dependent)", | 
|  | is_text: false, | 
|  | compatible_with_cgus_and_single_output: true | 
|  | }, | 
|  | LlvmAssembly => { | 
|  | shorthand: "llvm-ir", | 
|  | extension: "ll", | 
|  | description: "Generates a file containing LLVM IR", | 
|  | default_filename: "CRATE_NAME.ll", | 
|  | is_text: true, | 
|  | compatible_with_cgus_and_single_output: false | 
|  | }, | 
|  | Metadata => { | 
|  | shorthand: "metadata", | 
|  | extension: "rmeta", | 
|  | description: "Generates a file containing metadata about the crate", | 
|  | default_filename: "libCRATE_NAME.rmeta", | 
|  | is_text: false, | 
|  | compatible_with_cgus_and_single_output: true | 
|  | }, | 
|  | Mir => { | 
|  | shorthand: "mir", | 
|  | extension: "mir", | 
|  | description: "Generates a file containing rustc's mid-level intermediate representation", | 
|  | default_filename: "CRATE_NAME.mir", | 
|  | is_text: true, | 
|  | compatible_with_cgus_and_single_output: false | 
|  | }, | 
|  | Object => { | 
|  | shorthand: "obj", | 
|  | extension: "o", | 
|  | description: "Generates a native object file", | 
|  | default_filename: "CRATE_NAME.o", | 
|  | is_text: false, | 
|  | compatible_with_cgus_and_single_output: false | 
|  | }, | 
|  | #[doc = "This is the summary or index data part of the ThinLTO bitcode."] | 
|  | ThinLinkBitcode => { | 
|  | shorthand: "thin-link-bitcode", | 
|  | extension: "indexing.o", | 
|  | description: "Generates the ThinLTO summary as bitcode", | 
|  | default_filename: "CRATE_NAME.indexing.o", | 
|  | is_text: false, | 
|  | compatible_with_cgus_and_single_output: false | 
|  | }, | 
|  | } | 
|  |  | 
|  | /// The type of diagnostics output to generate. | 
|  | #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] | 
|  | pub enum ErrorOutputType { | 
|  | /// Output meant for the consumption of humans. | 
|  | #[default] | 
|  | HumanReadable { | 
|  | kind: HumanReadableErrorType = HumanReadableErrorType::Default, | 
|  | color_config: ColorConfig = ColorConfig::Auto, | 
|  | }, | 
|  | /// 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, | 
|  | color_config: ColorConfig, | 
|  | }, | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Hash, Debug)] | 
|  | pub enum ResolveDocLinks { | 
|  | /// Do not resolve doc links. | 
|  | None, | 
|  | /// Resolve doc links on exported items only for crate types that have metadata. | 
|  | ExportedMetadata, | 
|  | /// Resolve doc links on exported items. | 
|  | Exported, | 
|  | /// Resolve doc links on all items. | 
|  | All, | 
|  | } | 
|  |  | 
|  | /// 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. Also only hash keys, since tracking | 
|  | /// should only depend on the output types, not the paths they're written to. | 
|  | #[derive(Clone, Debug, Hash, HashStable_Generic, Encodable, Decodable)] | 
|  | pub struct OutputTypes(BTreeMap<OutputType, Option<OutFileName>>); | 
|  |  | 
|  | impl OutputTypes { | 
|  | pub fn new(entries: &[(OutputType, Option<OutFileName>)]) -> OutputTypes { | 
|  | OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) | 
|  | } | 
|  |  | 
|  | pub(crate) fn get(&self, key: &OutputType) -> Option<&Option<OutFileName>> { | 
|  | self.0.get(key) | 
|  | } | 
|  |  | 
|  | pub fn contains_key(&self, key: &OutputType) -> bool { | 
|  | self.0.contains_key(key) | 
|  | } | 
|  |  | 
|  | /// Returns `true` if user specified a name and not just produced type | 
|  | pub fn contains_explicit_name(&self, key: &OutputType) -> bool { | 
|  | matches!(self.0.get(key), Some(Some(..))) | 
|  | } | 
|  |  | 
|  | pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option<OutFileName>> { | 
|  | self.0.iter() | 
|  | } | 
|  |  | 
|  | pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<OutFileName>> { | 
|  | self.0.keys() | 
|  | } | 
|  |  | 
|  | pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<OutFileName>> { | 
|  | 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::ThinLinkBitcode | 
|  | | OutputType::Assembly | 
|  | | OutputType::LlvmAssembly | 
|  | | OutputType::Mir | 
|  | | OutputType::Object | 
|  | | OutputType::Exe => true, | 
|  | OutputType::Metadata | OutputType::DepInfo => false, | 
|  | }) | 
|  | } | 
|  |  | 
|  | /// Returns `true` if any of the output types require linking. | 
|  | pub fn should_link(&self) -> bool { | 
|  | self.0.keys().any(|k| match *k { | 
|  | OutputType::Bitcode | 
|  | | OutputType::ThinLinkBitcode | 
|  | | OutputType::Assembly | 
|  | | OutputType::LlvmAssembly | 
|  | | OutputType::Mir | 
|  | | OutputType::Metadata | 
|  | | OutputType::Object | 
|  | | OutputType::DepInfo => false, | 
|  | OutputType::Exe => true, | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// 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<String, ExternEntry>); | 
|  |  | 
|  | #[derive(Clone, Debug)] | 
|  | pub struct ExternEntry { | 
|  | pub location: ExternLocation, | 
|  | /// Indicates this is a "private" dependency for the | 
|  | /// `exported_private_dependencies` lint. | 
|  | /// | 
|  | /// This can be set with the `priv` option like | 
|  | /// `--extern priv:name=foo.rlib`. | 
|  | pub is_private_dep: bool, | 
|  | /// Add the extern entry to the extern prelude. | 
|  | /// | 
|  | /// This can be disabled with the `noprelude` option like | 
|  | /// `--extern noprelude:name`. | 
|  | pub add_prelude: bool, | 
|  | /// The extern entry shouldn't be considered for unused dependency warnings. | 
|  | /// | 
|  | /// `--extern nounused:std=/path/to/lib/libstd.rlib`. This is used to | 
|  | /// suppress `unused-crate-dependencies` warnings. | 
|  | pub nounused_dep: bool, | 
|  | /// If the extern entry is not referenced in the crate, force it to be resolved anyway. | 
|  | /// | 
|  | /// Allows a dependency satisfying, for instance, a missing panic handler to be injected | 
|  | /// without modifying source: | 
|  | /// `--extern force:extras=/path/to/lib/libstd.rlib` | 
|  | pub force: bool, | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Debug)] | 
|  | pub enum ExternLocation { | 
|  | /// Indicates to look for the library in the search paths. | 
|  | /// | 
|  | /// Added via `--extern name`. | 
|  | FoundInLibrarySearchDirectories, | 
|  | /// The locations where this extern entry must be found. | 
|  | /// | 
|  | /// The `CrateLoader` is responsible for loading these and figuring out | 
|  | /// which one to use. | 
|  | /// | 
|  | /// Added via `--extern prelude_name=some_file.rlib` | 
|  | ExactPaths(BTreeSet<CanonicalizedPath>), | 
|  | } | 
|  |  | 
|  | impl Externs { | 
|  | /// Used for testing. | 
|  | pub fn new(data: BTreeMap<String, ExternEntry>) -> 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() | 
|  | } | 
|  | } | 
|  |  | 
|  | impl ExternEntry { | 
|  | fn new(location: ExternLocation) -> ExternEntry { | 
|  | ExternEntry { | 
|  | location, | 
|  | is_private_dep: false, | 
|  | add_prelude: false, | 
|  | nounused_dep: false, | 
|  | force: false, | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> { | 
|  | match &self.location { | 
|  | ExternLocation::ExactPaths(set) => Some(set.iter()), | 
|  | _ => None, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, PartialEq, Debug)] | 
|  | pub struct PrintRequest { | 
|  | pub kind: PrintKind, | 
|  | pub out: OutFileName, | 
|  | } | 
|  |  | 
|  | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | 
|  | pub enum PrintKind { | 
|  | // tidy-alphabetical-start | 
|  | AllTargetSpecsJson, | 
|  | CallingConventions, | 
|  | Cfg, | 
|  | CheckCfg, | 
|  | CodeModels, | 
|  | CrateName, | 
|  | CrateRootLintLevels, | 
|  | DeploymentTarget, | 
|  | FileNames, | 
|  | HostTuple, | 
|  | LinkArgs, | 
|  | NativeStaticLibs, | 
|  | RelocationModels, | 
|  | SplitDebuginfo, | 
|  | StackProtectorStrategies, | 
|  | SupportedCrateTypes, | 
|  | Sysroot, | 
|  | TargetCPUs, | 
|  | TargetFeatures, | 
|  | TargetLibdir, | 
|  | TargetList, | 
|  | TargetSpecJson, | 
|  | TlsModels, | 
|  | // tidy-alphabetical-end | 
|  | } | 
|  |  | 
|  | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)] | 
|  | pub struct NextSolverConfig { | 
|  | /// Whether the new trait solver should be enabled in coherence. | 
|  | pub coherence: bool = true, | 
|  | /// Whether the new trait solver should be enabled everywhere. | 
|  | /// This is only `true` if `coherence` is also enabled. | 
|  | pub globally: bool = false, | 
|  | } | 
|  |  | 
|  | #[derive(Clone)] | 
|  | 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 { | 
|  | if let Input::File(ifile) = self { | 
|  | // If for some reason getting the file stem as a UTF-8 string fails, | 
|  | // then fallback to a fixed name. | 
|  | if let Some(name) = ifile.file_stem().and_then(OsStr::to_str) { | 
|  | return name; | 
|  | } | 
|  | } | 
|  | "rust_out" | 
|  | } | 
|  |  | 
|  | pub fn source_name(&self) -> FileName { | 
|  | match *self { | 
|  | Input::File(ref ifile) => ifile.clone().into(), | 
|  | Input::Str { ref name, .. } => name.clone(), | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn opt_path(&self) -> Option<&Path> { | 
|  | match self { | 
|  | Input::File(file) => Some(file), | 
|  | Input::Str { name, .. } => match name { | 
|  | FileName::Real(real) => real.local_path(), | 
|  | FileName::CfgSpec(_) => None, | 
|  | FileName::Anon(_) => None, | 
|  | FileName::MacroExpansion(_) => None, | 
|  | FileName::ProcMacroSourceCode(_) => None, | 
|  | FileName::CliCrateAttr(_) => None, | 
|  | FileName::Custom(_) => None, | 
|  | FileName::DocTest(path, _) => Some(path), | 
|  | FileName::InlineAsm(_) => None, | 
|  | }, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq, Encodable, Decodable)] | 
|  | pub enum OutFileName { | 
|  | Real(PathBuf), | 
|  | Stdout, | 
|  | } | 
|  |  | 
|  | impl OutFileName { | 
|  | pub fn parent(&self) -> Option<&Path> { | 
|  | match *self { | 
|  | OutFileName::Real(ref path) => path.parent(), | 
|  | OutFileName::Stdout => None, | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn filestem(&self) -> Option<&OsStr> { | 
|  | match *self { | 
|  | OutFileName::Real(ref path) => path.file_stem(), | 
|  | OutFileName::Stdout => Some(OsStr::new("stdout")), | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn is_stdout(&self) -> bool { | 
|  | match *self { | 
|  | OutFileName::Real(_) => false, | 
|  | OutFileName::Stdout => true, | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn is_tty(&self) -> bool { | 
|  | use std::io::IsTerminal; | 
|  | match *self { | 
|  | OutFileName::Real(_) => false, | 
|  | OutFileName::Stdout => std::io::stdout().is_terminal(), | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn as_path(&self) -> &Path { | 
|  | match *self { | 
|  | OutFileName::Real(ref path) => path.as_ref(), | 
|  | OutFileName::Stdout => Path::new("stdout"), | 
|  | } | 
|  | } | 
|  |  | 
|  | /// For a given output filename, return the actual name of the file that | 
|  | /// can be used to write codegen data of type `flavor`. For real-path | 
|  | /// output filenames, this would be trivial as we can just use the path. | 
|  | /// Otherwise for stdout, return a temporary path so that the codegen data | 
|  | /// may be later copied to stdout. | 
|  | pub fn file_for_writing( | 
|  | &self, | 
|  | outputs: &OutputFilenames, | 
|  | flavor: OutputType, | 
|  | codegen_unit_name: &str, | 
|  | invocation_temp: Option<&str>, | 
|  | ) -> PathBuf { | 
|  | match *self { | 
|  | OutFileName::Real(ref path) => path.clone(), | 
|  | OutFileName::Stdout => { | 
|  | outputs.temp_path_for_cgu(flavor, codegen_unit_name, invocation_temp) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn overwrite(&self, content: &str, sess: &Session) { | 
|  | match self { | 
|  | OutFileName::Stdout => print!("{content}"), | 
|  | OutFileName::Real(path) => { | 
|  | if let Err(e) = fs::write(path, content) { | 
|  | sess.dcx().emit_fatal(FileWriteFail { path, err: e.to_string() }); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Hash, Debug, HashStable_Generic, Encodable, Decodable)] | 
|  | pub struct OutputFilenames { | 
|  | pub(crate) out_directory: PathBuf, | 
|  | /// Crate name. Never contains '-'. | 
|  | crate_stem: String, | 
|  | /// Typically based on `.rs` input file name. Any '-' is preserved. | 
|  | filestem: String, | 
|  | pub single_output_file: Option<OutFileName>, | 
|  | temps_directory: Option<PathBuf>, | 
|  | pub outputs: OutputTypes, | 
|  | } | 
|  |  | 
|  | pub const RLINK_EXT: &str = "rlink"; | 
|  | pub const RUST_CGU_EXT: &str = "rcgu"; | 
|  | pub const DWARF_OBJECT_EXT: &str = "dwo"; | 
|  |  | 
|  | impl OutputFilenames { | 
|  | pub fn new( | 
|  | out_directory: PathBuf, | 
|  | out_crate_name: String, | 
|  | out_filestem: String, | 
|  | single_output_file: Option<OutFileName>, | 
|  | temps_directory: Option<PathBuf>, | 
|  | extra: String, | 
|  | outputs: OutputTypes, | 
|  | ) -> Self { | 
|  | OutputFilenames { | 
|  | out_directory, | 
|  | single_output_file, | 
|  | temps_directory, | 
|  | outputs, | 
|  | crate_stem: format!("{out_crate_name}{extra}"), | 
|  | filestem: format!("{out_filestem}{extra}"), | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn path(&self, flavor: OutputType) -> OutFileName { | 
|  | self.outputs | 
|  | .get(&flavor) | 
|  | .and_then(|p| p.to_owned()) | 
|  | .or_else(|| self.single_output_file.clone()) | 
|  | .unwrap_or_else(|| OutFileName::Real(self.output_path(flavor))) | 
|  | } | 
|  |  | 
|  | pub fn interface_path(&self) -> PathBuf { | 
|  | self.out_directory.join(format!("lib{}.rs", self.crate_stem)) | 
|  | } | 
|  |  | 
|  | /// Gets the output path where a compilation artifact of the given type | 
|  | /// should be placed on disk. | 
|  | fn output_path(&self, flavor: OutputType) -> PathBuf { | 
|  | let extension = flavor.extension(); | 
|  | match flavor { | 
|  | OutputType::Metadata => { | 
|  | self.out_directory.join(format!("lib{}.{}", self.crate_stem, extension)) | 
|  | } | 
|  | _ => self.with_directory_and_extension(&self.out_directory, extension), | 
|  | } | 
|  | } | 
|  |  | 
|  | /// 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_for_cgu( | 
|  | &self, | 
|  | flavor: OutputType, | 
|  | codegen_unit_name: &str, | 
|  | invocation_temp: Option<&str>, | 
|  | ) -> PathBuf { | 
|  | let extension = flavor.extension(); | 
|  | self.temp_path_ext_for_cgu(extension, codegen_unit_name, invocation_temp) | 
|  | } | 
|  |  | 
|  | /// Like `temp_path`, but specifically for dwarf objects. | 
|  | pub fn temp_path_dwo_for_cgu( | 
|  | &self, | 
|  | codegen_unit_name: &str, | 
|  | invocation_temp: Option<&str>, | 
|  | ) -> PathBuf { | 
|  | self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp) | 
|  | } | 
|  |  | 
|  | /// Like `temp_path`, but also supports things where there is no corresponding | 
|  | /// OutputType, like noopt-bitcode or lto-bitcode. | 
|  | pub fn temp_path_ext_for_cgu( | 
|  | &self, | 
|  | ext: &str, | 
|  | codegen_unit_name: &str, | 
|  | invocation_temp: Option<&str>, | 
|  | ) -> PathBuf { | 
|  | let mut extension = codegen_unit_name.to_string(); | 
|  |  | 
|  | // Append `.{invocation_temp}` to ensure temporary files are unique. | 
|  | if let Some(rng) = invocation_temp { | 
|  | extension.push('.'); | 
|  | extension.push_str(rng); | 
|  | } | 
|  |  | 
|  | // FIXME: This is sketchy that we're not appending `.rcgu` when the ext is empty. | 
|  | // Append `.rcgu.{ext}`. | 
|  | if !ext.is_empty() { | 
|  | extension.push('.'); | 
|  | extension.push_str(RUST_CGU_EXT); | 
|  | extension.push('.'); | 
|  | extension.push_str(ext); | 
|  | } | 
|  |  | 
|  | let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory); | 
|  | self.with_directory_and_extension(temps_directory, &extension) | 
|  | } | 
|  |  | 
|  | pub fn temp_path_for_diagnostic(&self, ext: &str) -> PathBuf { | 
|  | let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory); | 
|  | self.with_directory_and_extension(temps_directory, &ext) | 
|  | } | 
|  |  | 
|  | pub fn with_extension(&self, extension: &str) -> PathBuf { | 
|  | self.with_directory_and_extension(&self.out_directory, extension) | 
|  | } | 
|  |  | 
|  | pub fn with_directory_and_extension(&self, directory: &Path, extension: &str) -> PathBuf { | 
|  | let mut path = directory.join(&self.filestem); | 
|  | path.set_extension(extension); | 
|  | path | 
|  | } | 
|  |  | 
|  | /// Returns the path for the Split DWARF file - this can differ depending on which Split DWARF | 
|  | /// mode is being used, which is the logic that this function is intended to encapsulate. | 
|  | pub fn split_dwarf_path( | 
|  | &self, | 
|  | split_debuginfo_kind: SplitDebuginfo, | 
|  | split_dwarf_kind: SplitDwarfKind, | 
|  | cgu_name: &str, | 
|  | invocation_temp: Option<&str>, | 
|  | ) -> Option<PathBuf> { | 
|  | let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp); | 
|  | let dwo_out = self.temp_path_dwo_for_cgu(cgu_name, invocation_temp); | 
|  | match (split_debuginfo_kind, split_dwarf_kind) { | 
|  | (SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None, | 
|  | // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes | 
|  | // (pointing at the path which is being determined here). Use the path to the current | 
|  | // object file. | 
|  | (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => { | 
|  | Some(obj_out) | 
|  | } | 
|  | // Split mode emits the DWARF into a different file, use that path. | 
|  | (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => { | 
|  | Some(dwo_out) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bitflags::bitflags! { | 
|  | /// Scopes used to determined if it need to apply to --remap-path-prefix | 
|  | #[derive(Clone, Copy, PartialEq, Eq, Hash)] | 
|  | pub struct RemapPathScopeComponents: u8 { | 
|  | /// Apply remappings to the expansion of std::file!() macro | 
|  | const MACRO = 1 << 0; | 
|  | /// Apply remappings to printed compiler diagnostics | 
|  | const DIAGNOSTICS = 1 << 1; | 
|  | /// Apply remappings to debug information | 
|  | const DEBUGINFO = 1 << 3; | 
|  |  | 
|  | /// An alias for `macro` and `debuginfo`. This ensures all paths in compiled | 
|  | /// executables or libraries are remapped but not elsewhere. | 
|  | const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits(); | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Debug)] | 
|  | pub struct Sysroot { | 
|  | pub explicit: Option<PathBuf>, | 
|  | pub default: PathBuf, | 
|  | } | 
|  |  | 
|  | impl Sysroot { | 
|  | pub fn new(explicit: Option<PathBuf>) -> Sysroot { | 
|  | Sysroot { explicit, default: filesearch::default_sysroot() } | 
|  | } | 
|  |  | 
|  | /// Return explicit sysroot if it was passed with `--sysroot`, or default sysroot otherwise. | 
|  | pub fn path(&self) -> &Path { | 
|  | self.explicit.as_deref().unwrap_or(&self.default) | 
|  | } | 
|  |  | 
|  | /// Returns both explicit sysroot if it was passed with `--sysroot` and the default sysroot. | 
|  | pub fn all_paths(&self) -> impl Iterator<Item = &Path> { | 
|  | self.explicit.as_deref().into_iter().chain(iter::once(&*self.default)) | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn host_tuple() -> &'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") | 
|  | } | 
|  |  | 
|  | fn file_path_mapping( | 
|  | remap_path_prefix: Vec<(PathBuf, PathBuf)>, | 
|  | unstable_opts: &UnstableOptions, | 
|  | ) -> FilePathMapping { | 
|  | FilePathMapping::new( | 
|  | remap_path_prefix.clone(), | 
|  | if unstable_opts.remap_path_scope.contains(RemapPathScopeComponents::DIAGNOSTICS) | 
|  | && !remap_path_prefix.is_empty() | 
|  | { | 
|  | FileNameDisplayPreference::Remapped | 
|  | } else { | 
|  | FileNameDisplayPreference::Local | 
|  | }, | 
|  | if unstable_opts.remap_path_scope.is_all() { | 
|  | FileNameEmbeddablePreference::RemappedOnly | 
|  | } else { | 
|  | FileNameEmbeddablePreference::LocalAndRemapped | 
|  | }, | 
|  | ) | 
|  | } | 
|  |  | 
|  | impl Default for Options { | 
|  | fn default() -> Options { | 
|  | Options { | 
|  | assert_incr_state: None, | 
|  | crate_types: Vec::new(), | 
|  | optimize: OptLevel::No, | 
|  | debuginfo: DebugInfo::None, | 
|  | debuginfo_compression: DebugInfoCompression::None, | 
|  | lint_opts: Vec::new(), | 
|  | lint_cap: None, | 
|  | describe_lints: false, | 
|  | output_types: OutputTypes(BTreeMap::new()), | 
|  | search_paths: vec![], | 
|  | sysroot: Sysroot::new(None), | 
|  | target_triple: TargetTuple::from_tuple(host_tuple()), | 
|  | test: false, | 
|  | incremental: None, | 
|  | untracked_state_hash: Default::default(), | 
|  | unstable_opts: Default::default(), | 
|  | prints: Vec::new(), | 
|  | cg: Default::default(), | 
|  | error_format: ErrorOutputType::default(), | 
|  | diagnostic_width: None, | 
|  | externs: Externs(BTreeMap::new()), | 
|  | crate_name: None, | 
|  | libs: Vec::new(), | 
|  | unstable_features: UnstableFeatures::Disallow, | 
|  | debug_assertions: true, | 
|  | actually_rustdoc: false, | 
|  | resolve_doc_links: ResolveDocLinks::None, | 
|  | trimmed_def_paths: false, | 
|  | cli_forced_codegen_units: None, | 
|  | cli_forced_local_thinlto_off: false, | 
|  | remap_path_prefix: Vec::new(), | 
|  | real_rust_source_base_dir: None, | 
|  | real_rustc_dev_source_base_dir: None, | 
|  | edition: DEFAULT_EDITION, | 
|  | json_artifact_notifications: false, | 
|  | json_timings: false, | 
|  | json_unused_externs: JsonUnusedExterns::No, | 
|  | json_future_incompat: false, | 
|  | pretty: None, | 
|  | working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()), | 
|  | color: ColorConfig::Auto, | 
|  | logical_env: FxIndexMap::default(), | 
|  | verbose: false, | 
|  | target_modifiers: BTreeMap::default(), | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | 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.unstable_opts.dump_dep_graph | 
|  | || self.unstable_opts.query_dep_graph | 
|  | } | 
|  |  | 
|  | pub fn file_path_mapping(&self) -> FilePathMapping { | 
|  | file_path_mapping(self.remap_path_prefix.clone(), &self.unstable_opts) | 
|  | } | 
|  |  | 
|  | /// Returns `true` if there will be an output file generated. | 
|  | pub fn will_create_output_file(&self) -> bool { | 
|  | !self.unstable_opts.parse_crate_root_only && // The file is just being parsed | 
|  | self.unstable_opts.ls.is_empty() // The file is just being queried | 
|  | } | 
|  |  | 
|  | #[inline] | 
|  | pub fn share_generics(&self) -> bool { | 
|  | match self.unstable_opts.share_generics { | 
|  | Some(setting) => setting, | 
|  | None => match self.optimize { | 
|  | OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true, | 
|  | OptLevel::More | OptLevel::Aggressive => false, | 
|  | }, | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion { | 
|  | self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl UnstableOptions { | 
|  | pub fn dcx_flags(&self, can_emit_warnings: bool) -> DiagCtxtFlags { | 
|  | DiagCtxtFlags { | 
|  | can_emit_warnings, | 
|  | treat_err_as_bug: self.treat_err_as_bug, | 
|  | eagerly_emit_delayed_bugs: self.eagerly_emit_delayed_bugs, | 
|  | macro_backtrace: self.macro_backtrace, | 
|  | deduplicate_diagnostics: self.deduplicate_diagnostics, | 
|  | track_diagnostics: self.track_diagnostics, | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn src_hash_algorithm(&self, target: &Target) -> SourceFileHashAlgorithm { | 
|  | self.src_hash_algorithm.unwrap_or_else(|| { | 
|  | if target.is_like_msvc { | 
|  | SourceFileHashAlgorithm::Sha256 | 
|  | } else { | 
|  | SourceFileHashAlgorithm::Md5 | 
|  | } | 
|  | }) | 
|  | } | 
|  |  | 
|  | pub fn checksum_hash_algorithm(&self) -> Option<SourceFileHashAlgorithm> { | 
|  | self.checksum_hash_algorithm | 
|  | } | 
|  | } | 
|  |  | 
|  | // The type of entry function, so users can have their own entry functions | 
|  | #[derive(Copy, Clone, PartialEq, Hash, Debug, HashStable_Generic)] | 
|  | pub enum EntryFnType { | 
|  | Main { | 
|  | /// Specifies what to do with `SIGPIPE` before calling `fn main()`. | 
|  | /// | 
|  | /// What values that are valid and what they mean must be in sync | 
|  | /// across rustc and libstd, but we don't want it public in libstd, | 
|  | /// so we take a bit of an unusual approach with simple constants | 
|  | /// and an `include!()`. | 
|  | sigpipe: u8, | 
|  | }, | 
|  | } | 
|  |  | 
|  | #[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] | 
|  | #[derive(HashStable_Generic)] | 
|  | pub enum CrateType { | 
|  | Executable, | 
|  | Dylib, | 
|  | Rlib, | 
|  | Staticlib, | 
|  | Cdylib, | 
|  | ProcMacro, | 
|  | Sdylib, | 
|  | } | 
|  |  | 
|  | impl CrateType { | 
|  | pub fn has_metadata(self) -> bool { | 
|  | match self { | 
|  | CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true, | 
|  | CrateType::Executable | 
|  | | CrateType::Cdylib | 
|  | | CrateType::Staticlib | 
|  | | CrateType::Sdylib => false, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Hash, Debug, PartialEq, Eq)] | 
|  | pub enum Passes { | 
|  | Some(Vec<String>), | 
|  | All, | 
|  | } | 
|  |  | 
|  | impl Passes { | 
|  | fn is_empty(&self) -> bool { | 
|  | match *self { | 
|  | Passes::Some(ref v) => v.is_empty(), | 
|  | Passes::All => false, | 
|  | } | 
|  | } | 
|  |  | 
|  | pub(crate) fn extend(&mut self, passes: impl IntoIterator<Item = String>) { | 
|  | match *self { | 
|  | Passes::Some(ref mut v) => v.extend(passes), | 
|  | Passes::All => {} | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Copy, Hash, Debug, PartialEq)] | 
|  | pub enum PAuthKey { | 
|  | A, | 
|  | B, | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Copy, Hash, Debug, PartialEq)] | 
|  | pub struct PacRet { | 
|  | pub leaf: bool, | 
|  | pub pc: bool, | 
|  | pub key: PAuthKey, | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Copy, Hash, Debug, PartialEq, Default)] | 
|  | pub struct BranchProtection { | 
|  | pub bti: bool, | 
|  | pub pac_ret: Option<PacRet>, | 
|  | } | 
|  |  | 
|  | pub(crate) const fn default_lib_output() -> CrateType { | 
|  | CrateType::Rlib | 
|  | } | 
|  |  | 
|  | pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg { | 
|  | // First disallow some configuration given on the command line | 
|  | cfg::disallow_cfgs(sess, &user_cfg); | 
|  |  | 
|  | // Then combine the configuration requested by the session (command line) with | 
|  | // some default and generated configuration items. | 
|  | user_cfg.extend(cfg::default_configuration(sess)); | 
|  | user_cfg | 
|  | } | 
|  |  | 
|  | pub fn build_target_config( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | target: &TargetTuple, | 
|  | sysroot: &Path, | 
|  | ) -> Target { | 
|  | match Target::search(target, sysroot) { | 
|  | Ok((target, warnings)) => { | 
|  | for warning in warnings.warning_messages() { | 
|  | early_dcx.early_warn(warning) | 
|  | } | 
|  |  | 
|  | if !matches!(target.pointer_width, 16 | 32 | 64) { | 
|  | early_dcx.early_fatal(format!( | 
|  | "target specification was invalid: unrecognized target-pointer-width {}", | 
|  | target.pointer_width | 
|  | )) | 
|  | } | 
|  | target | 
|  | } | 
|  | Err(e) => { | 
|  | let mut err = | 
|  | early_dcx.early_struct_fatal(format!("error loading target specification: {e}")); | 
|  | err.help("run `rustc --print target-list` for a list of built-in targets"); | 
|  | err.emit(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | 
|  | pub enum OptionStability { | 
|  | Stable, | 
|  | Unstable, | 
|  | } | 
|  |  | 
|  | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | 
|  | pub enum OptionKind { | 
|  | /// An option that takes a value, and cannot appear more than once (e.g. `--out-dir`). | 
|  | /// | 
|  | /// Corresponds to [`getopts::Options::optopt`]. | 
|  | Opt, | 
|  |  | 
|  | /// An option that takes a value, and can appear multiple times (e.g. `--emit`). | 
|  | /// | 
|  | /// Corresponds to [`getopts::Options::optmulti`]. | 
|  | Multi, | 
|  |  | 
|  | /// An option that does not take a value, and cannot appear more than once (e.g. `--help`). | 
|  | /// | 
|  | /// Corresponds to [`getopts::Options::optflag`]. | 
|  | /// The `hint` string must be empty. | 
|  | Flag, | 
|  |  | 
|  | /// An option that does not take a value, and can appear multiple times (e.g. `-O`). | 
|  | /// | 
|  | /// Corresponds to [`getopts::Options::optflagmulti`]. | 
|  | /// The `hint` string must be empty. | 
|  | FlagMulti, | 
|  | } | 
|  |  | 
|  | pub struct RustcOptGroup { | 
|  | /// The "primary" name for this option. Normally equal to `long_name`, | 
|  | /// except for options that don't have a long name, in which case | 
|  | /// `short_name` is used. | 
|  | /// | 
|  | /// This is needed when interacting with `getopts` in some situations, | 
|  | /// because if an option has both forms, that library treats the long name | 
|  | /// as primary and the short name as an alias. | 
|  | pub name: &'static str, | 
|  | stability: OptionStability, | 
|  | kind: OptionKind, | 
|  |  | 
|  | short_name: &'static str, | 
|  | long_name: &'static str, | 
|  | desc: &'static str, | 
|  | value_hint: &'static str, | 
|  |  | 
|  | /// If true, this option should not be printed by `rustc --help`, but | 
|  | /// should still be printed by `rustc --help -v`. | 
|  | pub is_verbose_help_only: bool, | 
|  | } | 
|  |  | 
|  | impl RustcOptGroup { | 
|  | pub fn is_stable(&self) -> bool { | 
|  | self.stability == OptionStability::Stable | 
|  | } | 
|  |  | 
|  | pub fn apply(&self, options: &mut getopts::Options) { | 
|  | let &Self { short_name, long_name, desc, value_hint, .. } = self; | 
|  | match self.kind { | 
|  | OptionKind::Opt => options.optopt(short_name, long_name, desc, value_hint), | 
|  | OptionKind::Multi => options.optmulti(short_name, long_name, desc, value_hint), | 
|  | OptionKind::Flag => options.optflag(short_name, long_name, desc), | 
|  | OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc), | 
|  | }; | 
|  | } | 
|  |  | 
|  | /// This is for diagnostics-only. | 
|  | pub fn long_name(&self) -> &str { | 
|  | self.long_name | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn make_opt( | 
|  | stability: OptionStability, | 
|  | kind: OptionKind, | 
|  | short_name: &'static str, | 
|  | long_name: &'static str, | 
|  | desc: &'static str, | 
|  | value_hint: &'static str, | 
|  | ) -> RustcOptGroup { | 
|  | // "Flag" options don't have a value, and therefore don't have a value hint. | 
|  | match kind { | 
|  | OptionKind::Opt | OptionKind::Multi => {} | 
|  | OptionKind::Flag | OptionKind::FlagMulti => assert_eq!(value_hint, ""), | 
|  | } | 
|  | RustcOptGroup { | 
|  | name: cmp::max_by_key(short_name, long_name, |s| s.len()), | 
|  | stability, | 
|  | kind, | 
|  | short_name, | 
|  | long_name, | 
|  | desc, | 
|  | value_hint, | 
|  | is_verbose_help_only: false, | 
|  | } | 
|  | } | 
|  |  | 
|  | static EDITION_STRING: LazyLock<String> = LazyLock::new(|| { | 
|  | format!( | 
|  | "Specify which edition of the compiler to use when compiling code. \ | 
|  | The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE_EDITION}." | 
|  | ) | 
|  | }); | 
|  |  | 
|  | static PRINT_HELP: LazyLock<String> = LazyLock::new(|| { | 
|  | format!( | 
|  | "Compiler information to print on stdout (or to a file)\n\ | 
|  | INFO may be one of <{}>.", | 
|  | PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::<Vec<_>>().join("|") | 
|  | ) | 
|  | }); | 
|  |  | 
|  | static EMIT_HELP: LazyLock<String> = LazyLock::new(|| { | 
|  | let mut result = | 
|  | String::from("Comma separated list of types of output for the compiler to emit.\n"); | 
|  | result.push_str("Each TYPE has the default FILE name:\n"); | 
|  |  | 
|  | for output in OutputType::iter_all() { | 
|  | result.push_str(&format!("*  {} - {}\n", output.shorthand(), output.default_filename())); | 
|  | } | 
|  |  | 
|  | result | 
|  | }); | 
|  |  | 
|  | /// Returns all rustc command line options, including metadata for | 
|  | /// each option, such as whether the option is stable. | 
|  | /// | 
|  | /// # Option style guidelines | 
|  | /// | 
|  | /// - `<param>`: Indicates a required parameter | 
|  | /// - `[param]`: Indicates an optional parameter | 
|  | /// - `|`: Indicates a mutually exclusive option | 
|  | /// - `*`: a list element with description | 
|  | pub fn rustc_optgroups() -> Vec<RustcOptGroup> { | 
|  | use OptionKind::{Flag, FlagMulti, Multi, Opt}; | 
|  | use OptionStability::{Stable, Unstable}; | 
|  |  | 
|  | use self::make_opt as opt; | 
|  |  | 
|  | let mut options = vec![ | 
|  | opt(Stable, Flag, "h", "help", "Display this message", ""), | 
|  | opt( | 
|  | Stable, | 
|  | Multi, | 
|  | "", | 
|  | "cfg", | 
|  | "Configure the compilation environment.\n\ | 
|  | SPEC supports the syntax `<NAME>[=\"<VALUE>\"]`.", | 
|  | "<SPEC>", | 
|  | ), | 
|  | opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", "<SPEC>"), | 
|  | opt( | 
|  | Stable, | 
|  | Multi, | 
|  | "L", | 
|  | "", | 
|  | "Add a directory to the library search path. \ | 
|  | The optional KIND can be one of <dependency|crate|native|framework|all> (default: all).", | 
|  | "[<KIND>=]<PATH>", | 
|  | ), | 
|  | opt( | 
|  | Stable, | 
|  | Multi, | 
|  | "l", | 
|  | "", | 
|  | "Link the generated crate(s) to the specified native\n\ | 
|  | library NAME. The optional KIND can be one of\n\ | 
|  | <static|framework|dylib> (default: dylib).\n\ | 
|  | Optional comma separated MODIFIERS\n\ | 
|  | <bundle|verbatim|whole-archive|as-needed>\n\ | 
|  | may be specified each with a prefix of either '+' to\n\ | 
|  | enable or '-' to disable.", | 
|  | "[<KIND>[:<MODIFIERS>]=]<NAME>[:<RENAME>]", | 
|  | ), | 
|  | make_crate_type_option(), | 
|  | opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", "<NAME>"), | 
|  | opt(Stable, Opt, "", "edition", &EDITION_STRING, EDITION_NAME_LIST), | 
|  | opt(Stable, Multi, "", "emit", &EMIT_HELP, "<TYPE>[=<FILE>]"), | 
|  | opt(Stable, Multi, "", "print", &PRINT_HELP, "<INFO>[=<FILE>]"), | 
|  | opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""), | 
|  | opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=3", ""), | 
|  | opt(Stable, Opt, "o", "", "Write output to FILENAME", "<FILENAME>"), | 
|  | opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in DIR", "<DIR>"), | 
|  | opt( | 
|  | Stable, | 
|  | Opt, | 
|  | "", | 
|  | "explain", | 
|  | "Provide a detailed explanation of an error message", | 
|  | "<OPT>", | 
|  | ), | 
|  | opt(Stable, Flag, "", "test", "Build a test harness", ""), | 
|  | opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "<TARGET>"), | 
|  | opt(Stable, Multi, "A", "allow", "Set lint allowed", "<LINT>"), | 
|  | opt(Stable, Multi, "W", "warn", "Set lint warnings", "<LINT>"), | 
|  | opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "<LINT>"), | 
|  | opt(Stable, Multi, "D", "deny", "Set lint denied", "<LINT>"), | 
|  | opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "<LINT>"), | 
|  | opt( | 
|  | Stable, | 
|  | Multi, | 
|  | "", | 
|  | "cap-lints", | 
|  | "Set the most restrictive lint level. More restrictive lints are capped at this level", | 
|  | "<LEVEL>", | 
|  | ), | 
|  | opt(Stable, Multi, "C", "codegen", "Set a codegen option", "<OPT>[=<VALUE>]"), | 
|  | opt(Stable, Flag, "V", "version", "Print version info and exit", ""), | 
|  | opt(Stable, Flag, "v", "verbose", "Use verbose output", ""), | 
|  | ]; | 
|  |  | 
|  | // Options in this list are hidden from `rustc --help` by default, but are | 
|  | // shown by `rustc --help -v`. | 
|  | let verbose_only = [ | 
|  | opt( | 
|  | Stable, | 
|  | Multi, | 
|  | "", | 
|  | "extern", | 
|  | "Specify where an external rust library is located", | 
|  | "<NAME>[=<PATH>]", | 
|  | ), | 
|  | opt(Stable, Opt, "", "sysroot", "Override the system root", "<PATH>"), | 
|  | opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "<FLAG>"), | 
|  | opt( | 
|  | Stable, | 
|  | Opt, | 
|  | "", | 
|  | "error-format", | 
|  | "How errors and other messages are produced", | 
|  | "<human|json|short>", | 
|  | ), | 
|  | opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "<CONFIG>"), | 
|  | opt( | 
|  | Stable, | 
|  | Opt, | 
|  | "", | 
|  | "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( | 
|  | Stable, | 
|  | Opt, | 
|  | "", | 
|  | "diagnostic-width", | 
|  | "Inform rustc of the width of the output so that diagnostics can be truncated to fit", | 
|  | "<WIDTH>", | 
|  | ), | 
|  | opt( | 
|  | Stable, | 
|  | Multi, | 
|  | "", | 
|  | "remap-path-prefix", | 
|  | "Remap source names in all output (compiler messages and output files)", | 
|  | "<FROM>=<TO>", | 
|  | ), | 
|  | opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "<VAR>=<VALUE>"), | 
|  | ]; | 
|  | options.extend(verbose_only.into_iter().map(|mut opt| { | 
|  | opt.is_verbose_help_only = true; | 
|  | opt | 
|  | })); | 
|  |  | 
|  | options | 
|  | } | 
|  |  | 
|  | pub fn get_cmd_lint_options( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | matches: &getopts::Matches, | 
|  | ) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) { | 
|  | let mut lint_opts_with_position = vec![]; | 
|  | let mut describe_lints = false; | 
|  |  | 
|  | for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] { | 
|  | for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { | 
|  | if lint_name == "help" { | 
|  | describe_lints = true; | 
|  | } else { | 
|  | lint_opts_with_position.push((arg_pos, lint_name.replace('-', "_"), level)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | lint_opts_with_position.sort_by_key(|x| x.0); | 
|  | let lint_opts = lint_opts_with_position | 
|  | .iter() | 
|  | .cloned() | 
|  | .map(|(_, lint_name, level)| (lint_name, level)) | 
|  | .collect(); | 
|  |  | 
|  | let lint_cap = matches.opt_str("cap-lints").map(|cap| { | 
|  | lint::Level::from_str(&cap) | 
|  | .unwrap_or_else(|| early_dcx.early_fatal(format!("unknown lint level: `{cap}`"))) | 
|  | }); | 
|  |  | 
|  | (lint_opts, describe_lints, lint_cap) | 
|  | } | 
|  |  | 
|  | /// Parses the `--color` flag. | 
|  | pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> ColorConfig { | 
|  | match matches.opt_str("color").as_deref() { | 
|  | Some("auto") => ColorConfig::Auto, | 
|  | Some("always") => ColorConfig::Always, | 
|  | Some("never") => ColorConfig::Never, | 
|  |  | 
|  | None => ColorConfig::Auto, | 
|  |  | 
|  | Some(arg) => early_dcx.early_fatal(format!( | 
|  | "argument for `--color` must be auto, \ | 
|  | always or never (instead was `{arg}`)" | 
|  | )), | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Possible json config files | 
|  | pub struct JsonConfig { | 
|  | pub json_rendered: HumanReadableErrorType, | 
|  | pub json_color: ColorConfig, | 
|  | json_artifact_notifications: bool, | 
|  | /// Output start and end timestamps of several high-level compilation sections | 
|  | /// (frontend, backend, linker). | 
|  | json_timings: bool, | 
|  | pub json_unused_externs: JsonUnusedExterns, | 
|  | json_future_incompat: bool, | 
|  | } | 
|  |  | 
|  | /// Report unused externs in event stream | 
|  | #[derive(Copy, Clone)] | 
|  | pub enum JsonUnusedExterns { | 
|  | /// Do not | 
|  | No, | 
|  | /// Report, but do not exit with failure status for deny/forbid | 
|  | Silent, | 
|  | /// Report, and also exit with failure status for deny/forbid | 
|  | Loud, | 
|  | } | 
|  |  | 
|  | impl JsonUnusedExterns { | 
|  | pub fn is_enabled(&self) -> bool { | 
|  | match self { | 
|  | JsonUnusedExterns::No => false, | 
|  | JsonUnusedExterns::Loud | JsonUnusedExterns::Silent => true, | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn is_loud(&self) -> bool { | 
|  | match self { | 
|  | JsonUnusedExterns::No | JsonUnusedExterns::Silent => false, | 
|  | JsonUnusedExterns::Loud => true, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// 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(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> JsonConfig { | 
|  | let mut json_rendered = HumanReadableErrorType::Default; | 
|  | let mut json_color = ColorConfig::Never; | 
|  | let mut json_artifact_notifications = false; | 
|  | let mut json_unused_externs = JsonUnusedExterns::No; | 
|  | let mut json_future_incompat = false; | 
|  | let mut json_timings = 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_dcx.early_fatal("cannot specify the `--color` option with `--json`"); | 
|  | } | 
|  |  | 
|  | for sub_option in option.split(',') { | 
|  | match sub_option { | 
|  | "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, | 
|  | "diagnostic-unicode" => { | 
|  | json_rendered = HumanReadableErrorType::Unicode; | 
|  | } | 
|  | "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, | 
|  | "artifacts" => json_artifact_notifications = true, | 
|  | "timings" => json_timings = true, | 
|  | "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud, | 
|  | "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent, | 
|  | "future-incompat" => json_future_incompat = true, | 
|  | s => early_dcx.early_fatal(format!("unknown `--json` option `{s}`")), | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | JsonConfig { | 
|  | json_rendered, | 
|  | json_color, | 
|  | json_artifact_notifications, | 
|  | json_timings, | 
|  | json_unused_externs, | 
|  | json_future_incompat, | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Parses the `--error-format` flag. | 
|  | pub fn parse_error_format( | 
|  | early_dcx: &mut EarlyDiagCtxt, | 
|  | matches: &getopts::Matches, | 
|  | color_config: ColorConfig, | 
|  | json_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_deref() { | 
|  | None | Some("human") => ErrorOutputType::HumanReadable { color_config, .. }, | 
|  | Some("human-annotate-rs") => ErrorOutputType::HumanReadable { | 
|  | kind: HumanReadableErrorType::AnnotateSnippet, | 
|  | color_config, | 
|  | }, | 
|  | Some("json") => { | 
|  | ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color } | 
|  | } | 
|  | Some("pretty-json") => { | 
|  | ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color } | 
|  | } | 
|  | Some("short") => { | 
|  | ErrorOutputType::HumanReadable { kind: HumanReadableErrorType::Short, color_config } | 
|  | } | 
|  | Some("human-unicode") => ErrorOutputType::HumanReadable { | 
|  | kind: HumanReadableErrorType::Unicode, | 
|  | color_config, | 
|  | }, | 
|  | Some(arg) => { | 
|  | early_dcx.set_error_format(ErrorOutputType::HumanReadable { color_config, .. }); | 
|  | early_dcx.early_fatal(format!( | 
|  | "argument for `--error-format` must be `human`, `human-annotate-rs`, \ | 
|  | `human-unicode`, `json`, `pretty-json` or `short` (instead was `{arg}`)" | 
|  | )) | 
|  | } | 
|  | } | 
|  | } else { | 
|  | ErrorOutputType::HumanReadable { color_config, .. } | 
|  | }; | 
|  |  | 
|  | 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").is_empty() => { | 
|  | early_dcx.early_fatal("using `--json` requires also using `--error-format=json`"); | 
|  | } | 
|  |  | 
|  | _ => {} | 
|  | } | 
|  |  | 
|  | error_format | 
|  | } | 
|  |  | 
|  | pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Edition { | 
|  | let edition = match matches.opt_str("edition") { | 
|  | Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { | 
|  | early_dcx.early_fatal(format!( | 
|  | "argument for `--edition` must be one of: \ | 
|  | {EDITION_NAME_LIST}. (instead was `{arg}`)" | 
|  | )) | 
|  | }), | 
|  | None => DEFAULT_EDITION, | 
|  | }; | 
|  |  | 
|  | if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) { | 
|  | let is_nightly = nightly_options::match_is_nightly_build(matches); | 
|  | let msg = if !is_nightly { | 
|  | format!( | 
|  | "the crate requires edition {edition}, but the latest edition supported by this Rust version is {LATEST_STABLE_EDITION}" | 
|  | ) | 
|  | } else { | 
|  | format!("edition {edition} is unstable and only available with -Z unstable-options") | 
|  | }; | 
|  | early_dcx.early_fatal(msg) | 
|  | } | 
|  |  | 
|  | edition | 
|  | } | 
|  |  | 
|  | fn check_error_format_stability( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | unstable_opts: &UnstableOptions, | 
|  | format: ErrorOutputType, | 
|  | ) { | 
|  | if unstable_opts.unstable_options { | 
|  | return; | 
|  | } | 
|  | let format = match format { | 
|  | ErrorOutputType::Json { pretty: true, .. } => "pretty-json", | 
|  | ErrorOutputType::HumanReadable { kind, .. } => match kind { | 
|  | HumanReadableErrorType::AnnotateSnippet => "human-annotate-rs", | 
|  | HumanReadableErrorType::Unicode => "human-unicode", | 
|  | _ => return, | 
|  | }, | 
|  | _ => return, | 
|  | }; | 
|  | early_dcx.early_fatal(format!("`--error-format={format}` is unstable")) | 
|  | } | 
|  |  | 
|  | fn parse_output_types( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | unstable_opts: &UnstableOptions, | 
|  | matches: &getopts::Matches, | 
|  | ) -> OutputTypes { | 
|  | let mut output_types = BTreeMap::new(); | 
|  | if !unstable_opts.parse_crate_root_only { | 
|  | for list in matches.opt_strs("emit") { | 
|  | for output_type in list.split(',') { | 
|  | let (shorthand, path) = split_out_file_name(output_type); | 
|  | let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { | 
|  | early_dcx.early_fatal(format!( | 
|  | "unknown emission type: `{shorthand}` - expected one of: {display}", | 
|  | display = OutputType::shorthands_display(), | 
|  | )) | 
|  | }); | 
|  | if output_type == OutputType::ThinLinkBitcode && !unstable_opts.unstable_options { | 
|  | early_dcx.early_fatal(format!( | 
|  | "{} requested but -Zunstable-options not specified", | 
|  | OutputType::ThinLinkBitcode.shorthand() | 
|  | )); | 
|  | } | 
|  | output_types.insert(output_type, path); | 
|  | } | 
|  | } | 
|  | }; | 
|  | if output_types.is_empty() { | 
|  | output_types.insert(OutputType::Exe, None); | 
|  | } | 
|  | OutputTypes(output_types) | 
|  | } | 
|  |  | 
|  | fn split_out_file_name(arg: &str) -> (&str, Option<OutFileName>) { | 
|  | match arg.split_once('=') { | 
|  | None => (arg, None), | 
|  | Some((kind, "-")) => (kind, Some(OutFileName::Stdout)), | 
|  | Some((kind, path)) => (kind, Some(OutFileName::Real(PathBuf::from(path)))), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn should_override_cgus_and_disable_thinlto( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | output_types: &OutputTypes, | 
|  | matches: &getopts::Matches, | 
|  | mut codegen_units: Option<usize>, | 
|  | ) -> (bool, Option<usize>) { | 
|  | let mut disable_local_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_dcx.early_warn(format!( | 
|  | "`--emit={ot}` with `-o` incompatible with \ | 
|  | `-C codegen-units=N` for N > 1", | 
|  | )); | 
|  | } | 
|  | early_dcx.early_warn("resetting to default -C codegen-units=1"); | 
|  | codegen_units = Some(1); | 
|  | disable_local_thinlto = true; | 
|  | } | 
|  | } | 
|  | _ => { | 
|  | codegen_units = Some(1); | 
|  | disable_local_thinlto = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if codegen_units == Some(0) { | 
|  | early_dcx.early_fatal("value for codegen units must be a positive non-zero integer"); | 
|  | } | 
|  |  | 
|  | (disable_local_thinlto, codegen_units) | 
|  | } | 
|  |  | 
|  | fn collect_print_requests( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | cg: &mut CodegenOptions, | 
|  | unstable_opts: &UnstableOptions, | 
|  | matches: &getopts::Matches, | 
|  | ) -> Vec<PrintRequest> { | 
|  | let mut prints = Vec::<PrintRequest>::new(); | 
|  | if cg.target_cpu.as_deref() == Some("help") { | 
|  | prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout }); | 
|  | cg.target_cpu = None; | 
|  | }; | 
|  | if cg.target_feature == "help" { | 
|  | prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout }); | 
|  | cg.target_feature = String::new(); | 
|  | } | 
|  |  | 
|  | // We disallow reusing the same path in multiple prints, such as `--print | 
|  | // cfg=output.txt --print link-args=output.txt`, because outputs are printed | 
|  | // by disparate pieces of the compiler, and keeping track of which files | 
|  | // need to be overwritten vs appended to is annoying. | 
|  | let mut printed_paths = FxHashSet::default(); | 
|  |  | 
|  | prints.extend(matches.opt_strs("print").into_iter().map(|req| { | 
|  | let (req, out) = split_out_file_name(&req); | 
|  |  | 
|  | let kind = if let Some((print_name, print_kind)) = | 
|  | PRINT_KINDS.iter().find(|&&(name, _)| name == req) | 
|  | { | 
|  | check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind)); | 
|  | *print_kind | 
|  | } else { | 
|  | let is_nightly = nightly_options::match_is_nightly_build(matches); | 
|  | emit_unknown_print_request_help(early_dcx, req, is_nightly) | 
|  | }; | 
|  |  | 
|  | let out = out.unwrap_or(OutFileName::Stdout); | 
|  | if let OutFileName::Real(path) = &out { | 
|  | if !printed_paths.insert(path.clone()) { | 
|  | early_dcx.early_fatal(format!( | 
|  | "cannot print multiple outputs to the same path: {}", | 
|  | path.display(), | 
|  | )); | 
|  | } | 
|  | } | 
|  |  | 
|  | PrintRequest { kind, out } | 
|  | })); | 
|  |  | 
|  | prints | 
|  | } | 
|  |  | 
|  | fn check_print_request_stability( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | unstable_opts: &UnstableOptions, | 
|  | (print_name, print_kind): (&str, PrintKind), | 
|  | ) { | 
|  | if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options { | 
|  | early_dcx.early_fatal(format!( | 
|  | "the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \ | 
|  | print option" | 
|  | )); | 
|  | } | 
|  | } | 
|  |  | 
|  | fn is_print_request_stable(print_kind: PrintKind) -> bool { | 
|  | match print_kind { | 
|  | PrintKind::AllTargetSpecsJson | 
|  | | PrintKind::CheckCfg | 
|  | | PrintKind::CrateRootLintLevels | 
|  | | PrintKind::SupportedCrateTypes | 
|  | | PrintKind::TargetSpecJson => false, | 
|  | _ => true, | 
|  | } | 
|  | } | 
|  |  | 
|  | fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { | 
|  | let prints = PRINT_KINDS | 
|  | .iter() | 
|  | .filter_map(|(name, kind)| { | 
|  | // If we're not on nightly, we don't want to print unstable options | 
|  | if !is_nightly && !is_print_request_stable(*kind) { | 
|  | None | 
|  | } else { | 
|  | Some(format!("`{name}`")) | 
|  | } | 
|  | }) | 
|  | .collect::<Vec<_>>(); | 
|  | let prints = prints.join(", "); | 
|  |  | 
|  | let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); | 
|  | #[allow(rustc::diagnostic_outside_of_impl)] | 
|  | diag.help(format!("valid print requests are: {prints}")); | 
|  |  | 
|  | if req == "lints" { | 
|  | diag.help(format!("use `-Whelp` to print a list of lints")); | 
|  | } | 
|  |  | 
|  | diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information")); | 
|  | diag.emit() | 
|  | } | 
|  |  | 
|  | pub fn parse_target_triple(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> TargetTuple { | 
|  | match matches.opt_str("target") { | 
|  | Some(target) if target.ends_with(".json") => { | 
|  | let path = Path::new(&target); | 
|  | TargetTuple::from_path(path).unwrap_or_else(|_| { | 
|  | early_dcx.early_fatal(format!("target file {path:?} does not exist")) | 
|  | }) | 
|  | } | 
|  | Some(target) => TargetTuple::TargetTuple(target), | 
|  | _ => TargetTuple::from_tuple(host_tuple()), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn parse_opt_level( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | matches: &getopts::Matches, | 
|  | cg: &CodegenOptions, | 
|  | ) -> 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)| { | 
|  | // NB: This can match a string without `=`. | 
|  | if let Some("opt-level") = s.split('=').next() { Some(i) } else { None } | 
|  | }) | 
|  | .max(); | 
|  | if max_o > max_c { | 
|  | OptLevel::Aggressive | 
|  | } else { | 
|  | match cg.opt_level.as_ref() { | 
|  | "0" => OptLevel::No, | 
|  | "1" => OptLevel::Less, | 
|  | "2" => OptLevel::More, | 
|  | "3" => OptLevel::Aggressive, | 
|  | "s" => OptLevel::Size, | 
|  | "z" => OptLevel::SizeMin, | 
|  | arg => { | 
|  | early_dcx.early_fatal(format!( | 
|  | "optimization level needs to be \ | 
|  | between 0-3, s or z (instead was `{arg}`)" | 
|  | )); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> 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)| { | 
|  | // NB: This can match a string without `=`. | 
|  | if let Some("debuginfo") = s.split('=').next() { Some(i) } else { None } | 
|  | }) | 
|  | .max(); | 
|  | if max_g > max_c { DebugInfo::Full } else { cg.debuginfo } | 
|  | } | 
|  |  | 
|  | fn parse_assert_incr_state( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | opt_assertion: &Option<String>, | 
|  | ) -> Option<IncrementalStateAssertion> { | 
|  | match opt_assertion { | 
|  | Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded), | 
|  | Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded), | 
|  | Some(s) => { | 
|  | early_dcx.early_fatal(format!("unexpected incremental state assertion value: {s}")) | 
|  | } | 
|  | None => None, | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn parse_externs( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | matches: &getopts::Matches, | 
|  | unstable_opts: &UnstableOptions, | 
|  | ) -> Externs { | 
|  | let is_unstable_enabled = unstable_opts.unstable_options; | 
|  | let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new(); | 
|  | for arg in matches.opt_strs("extern") { | 
|  | let ExternOpt { crate_name: name, path, options } = | 
|  | split_extern_opt(early_dcx, unstable_opts, &arg).unwrap_or_else(|e| e.emit()); | 
|  |  | 
|  | let entry = externs.entry(name.to_owned()); | 
|  |  | 
|  | use std::collections::btree_map::Entry; | 
|  |  | 
|  | let entry = if let Some(path) = path { | 
|  | // --extern prelude_name=some_file.rlib | 
|  | let path = CanonicalizedPath::new(path); | 
|  | match entry { | 
|  | Entry::Vacant(vacant) => { | 
|  | let files = BTreeSet::from_iter(iter::once(path)); | 
|  | vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files))) | 
|  | } | 
|  | Entry::Occupied(occupied) => { | 
|  | let ext_ent = occupied.into_mut(); | 
|  | match ext_ent { | 
|  | ExternEntry { location: ExternLocation::ExactPaths(files), .. } => { | 
|  | files.insert(path); | 
|  | } | 
|  | ExternEntry { | 
|  | location: location @ ExternLocation::FoundInLibrarySearchDirectories, | 
|  | .. | 
|  | } => { | 
|  | // Exact paths take precedence over search directories. | 
|  | let files = BTreeSet::from_iter(iter::once(path)); | 
|  | *location = ExternLocation::ExactPaths(files); | 
|  | } | 
|  | } | 
|  | ext_ent | 
|  | } | 
|  | } | 
|  | } else { | 
|  | // --extern prelude_name | 
|  | match entry { | 
|  | Entry::Vacant(vacant) => { | 
|  | vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories)) | 
|  | } | 
|  | Entry::Occupied(occupied) => { | 
|  | // Ignore if already specified. | 
|  | occupied.into_mut() | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | let mut is_private_dep = false; | 
|  | let mut add_prelude = true; | 
|  | let mut nounused_dep = false; | 
|  | let mut force = false; | 
|  | if let Some(opts) = options { | 
|  | if !is_unstable_enabled { | 
|  | early_dcx.early_fatal( | 
|  | "the `-Z unstable-options` flag must also be passed to \ | 
|  | enable `--extern` options", | 
|  | ); | 
|  | } | 
|  | for opt in opts.split(',') { | 
|  | match opt { | 
|  | "priv" => is_private_dep = true, | 
|  | "noprelude" => { | 
|  | if let ExternLocation::ExactPaths(_) = &entry.location { | 
|  | add_prelude = false; | 
|  | } else { | 
|  | early_dcx.early_fatal( | 
|  | "the `noprelude` --extern option requires a file path", | 
|  | ); | 
|  | } | 
|  | } | 
|  | "nounused" => nounused_dep = true, | 
|  | "force" => force = true, | 
|  | _ => early_dcx.early_fatal(format!("unknown --extern option `{opt}`")), | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Crates start out being not private, and go to being private `priv` | 
|  | // is specified. | 
|  | entry.is_private_dep |= is_private_dep; | 
|  | // likewise `nounused` | 
|  | entry.nounused_dep |= nounused_dep; | 
|  | // and `force` | 
|  | entry.force |= force; | 
|  | // If any flag is missing `noprelude`, then add to the prelude. | 
|  | entry.add_prelude |= add_prelude; | 
|  | } | 
|  | Externs(externs) | 
|  | } | 
|  |  | 
|  | fn parse_remap_path_prefix( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | matches: &getopts::Matches, | 
|  | unstable_opts: &UnstableOptions, | 
|  | ) -> Vec<(PathBuf, PathBuf)> { | 
|  | let mut mapping: Vec<(PathBuf, PathBuf)> = matches | 
|  | .opt_strs("remap-path-prefix") | 
|  | .into_iter() | 
|  | .map(|remap| match remap.rsplit_once('=') { | 
|  | None => { | 
|  | early_dcx.early_fatal("--remap-path-prefix must contain '=' between FROM and TO") | 
|  | } | 
|  | Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)), | 
|  | }) | 
|  | .collect(); | 
|  | match &unstable_opts.remap_cwd_prefix { | 
|  | Some(to) => match std::env::current_dir() { | 
|  | Ok(cwd) => mapping.push((cwd, to.clone())), | 
|  | Err(_) => (), | 
|  | }, | 
|  | None => (), | 
|  | }; | 
|  | mapping | 
|  | } | 
|  |  | 
|  | fn parse_logical_env( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | matches: &getopts::Matches, | 
|  | ) -> FxIndexMap<String, String> { | 
|  | let mut vars = FxIndexMap::default(); | 
|  |  | 
|  | for arg in matches.opt_strs("env-set") { | 
|  | if let Some((name, val)) = arg.split_once('=') { | 
|  | vars.insert(name.to_string(), val.to_string()); | 
|  | } else { | 
|  | early_dcx.early_fatal(format!("`--env-set`: specify value for variable `{arg}`")); | 
|  | } | 
|  | } | 
|  |  | 
|  | vars | 
|  | } | 
|  |  | 
|  | // JUSTIFICATION: before wrapper fn is available | 
|  | #[allow(rustc::bad_opt_access)] | 
|  | pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches) -> Options { | 
|  | let color = parse_color(early_dcx, matches); | 
|  |  | 
|  | let edition = parse_crate_edition(early_dcx, matches); | 
|  |  | 
|  | let JsonConfig { | 
|  | json_rendered, | 
|  | json_color, | 
|  | json_artifact_notifications, | 
|  | json_timings, | 
|  | json_unused_externs, | 
|  | json_future_incompat, | 
|  | } = parse_json(early_dcx, matches); | 
|  |  | 
|  | let error_format = parse_error_format(early_dcx, matches, color, json_color, json_rendered); | 
|  |  | 
|  | early_dcx.set_error_format(error_format); | 
|  |  | 
|  | let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| { | 
|  | early_dcx.early_fatal("`--diagnostic-width` must be an positive integer"); | 
|  | }); | 
|  |  | 
|  | 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_dcx.early_fatal(e)); | 
|  |  | 
|  | let mut target_modifiers = BTreeMap::<OptionsTargetModifiers, String>::new(); | 
|  |  | 
|  | let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers); | 
|  | let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches); | 
|  |  | 
|  | if !unstable_opts.unstable_options && json_timings { | 
|  | early_dcx.early_fatal("--json=timings is unstable and requires using `-Zunstable-options`"); | 
|  | } | 
|  |  | 
|  | check_error_format_stability(early_dcx, &unstable_opts, error_format); | 
|  |  | 
|  | let output_types = parse_output_types(early_dcx, &unstable_opts, matches); | 
|  |  | 
|  | let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers); | 
|  | let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( | 
|  | early_dcx, | 
|  | &output_types, | 
|  | matches, | 
|  | cg.codegen_units, | 
|  | ); | 
|  |  | 
|  | if unstable_opts.threads == 0 { | 
|  | early_dcx.early_fatal("value for threads must be a positive non-zero integer"); | 
|  | } | 
|  |  | 
|  | if unstable_opts.threads == parse::MAX_THREADS_CAP { | 
|  | early_dcx.early_warn(format!("number of threads was capped at {}", parse::MAX_THREADS_CAP)); | 
|  | } | 
|  |  | 
|  | let incremental = cg.incremental.as_ref().map(PathBuf::from); | 
|  |  | 
|  | let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state); | 
|  |  | 
|  | if cg.profile_generate.enabled() && cg.profile_use.is_some() { | 
|  | early_dcx.early_fatal("options `-C profile-generate` and `-C profile-use` are exclusive"); | 
|  | } | 
|  |  | 
|  | if unstable_opts.profile_sample_use.is_some() | 
|  | && (cg.profile_generate.enabled() || cg.profile_use.is_some()) | 
|  | { | 
|  | early_dcx.early_fatal( | 
|  | "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`", | 
|  | ); | 
|  | } | 
|  |  | 
|  | // Check for unstable values of `-C symbol-mangling-version`. | 
|  | // This is what prevents them from being used on stable compilers. | 
|  | match cg.symbol_mangling_version { | 
|  | // Stable values: | 
|  | None | Some(SymbolManglingVersion::V0) => {} | 
|  |  | 
|  | // Unstable values: | 
|  | Some(SymbolManglingVersion::Legacy) => { | 
|  | if !unstable_opts.unstable_options { | 
|  | early_dcx.early_fatal( | 
|  | "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`", | 
|  | ); | 
|  | } | 
|  | } | 
|  | Some(SymbolManglingVersion::Hashed) => { | 
|  | if !unstable_opts.unstable_options { | 
|  | early_dcx.early_fatal( | 
|  | "`-C symbol-mangling-version=hashed` requires `-Z unstable-options`", | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if cg.instrument_coverage != InstrumentCoverage::No { | 
|  | if cg.profile_generate.enabled() || cg.profile_use.is_some() { | 
|  | early_dcx.early_fatal( | 
|  | "option `-C instrument-coverage` is not compatible with either `-C profile-use` \ | 
|  | or `-C profile-generate`", | 
|  | ); | 
|  | } | 
|  |  | 
|  | // `-C instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent | 
|  | // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over | 
|  | // multiple runs, including some changes to source code; so mangled names must be consistent | 
|  | // across compilations. | 
|  | match cg.symbol_mangling_version { | 
|  | None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0), | 
|  | Some(SymbolManglingVersion::Legacy) => { | 
|  | early_dcx.early_warn( | 
|  | "-C instrument-coverage requires symbol mangling version `v0`, \ | 
|  | but `-C symbol-mangling-version=legacy` was specified", | 
|  | ); | 
|  | } | 
|  | Some(SymbolManglingVersion::V0) => {} | 
|  | Some(SymbolManglingVersion::Hashed) => { | 
|  | early_dcx.early_warn( | 
|  | "-C instrument-coverage requires symbol mangling version `v0`, \ | 
|  | but `-C symbol-mangling-version=hashed` was specified", | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") { | 
|  | // FIXME: this is only mutation of UnstableOptions here, move into | 
|  | // UnstableOptions::build? | 
|  | unstable_opts.graphviz_font = graphviz_font; | 
|  | } | 
|  |  | 
|  | if !cg.embed_bitcode { | 
|  | match cg.lto { | 
|  | LtoCli::No | LtoCli::Unspecified => {} | 
|  | LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => { | 
|  | early_dcx.early_fatal("options `-C embed-bitcode=no` and `-C lto` are incompatible") | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | let unstable_options_enabled = nightly_options::is_unstable_enabled(matches); | 
|  | if !unstable_options_enabled && cg.force_frame_pointers == FramePointer::NonLeaf { | 
|  | early_dcx.early_fatal( | 
|  | "`-Cforce-frame-pointers=non-leaf` or `always` also requires `-Zunstable-options` \ | 
|  | and a nightly compiler", | 
|  | ) | 
|  | } | 
|  |  | 
|  | if !nightly_options::is_unstable_enabled(matches) | 
|  | && unstable_opts.offload.contains(&Offload::Enable) | 
|  | { | 
|  | early_dcx.early_fatal( | 
|  | "`-Zoffload=Enable` also requires `-Zunstable-options` \ | 
|  | and a nightly compiler", | 
|  | ) | 
|  | } | 
|  |  | 
|  | let target_triple = parse_target_triple(early_dcx, matches); | 
|  |  | 
|  | // Ensure `-Z unstable-options` is required when using the unstable `-C link-self-contained` and | 
|  | // `-C linker-flavor` options. | 
|  | if !unstable_options_enabled { | 
|  | if let Err(error) = cg.link_self_contained.check_unstable_variants(&target_triple) { | 
|  | early_dcx.early_fatal(error); | 
|  | } | 
|  |  | 
|  | if let Some(flavor) = cg.linker_flavor { | 
|  | if flavor.is_unstable() { | 
|  | early_dcx.early_fatal(format!( | 
|  | "the linker flavor `{}` is unstable, the `-Z unstable-options` \ | 
|  | flag must also be passed to use the unstable values", | 
|  | flavor.desc() | 
|  | )); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check `-C link-self-contained` for consistency: individual components cannot be both enabled | 
|  | // and disabled at the same time. | 
|  | if let Some(erroneous_components) = cg.link_self_contained.check_consistency() { | 
|  | let names: String = erroneous_components | 
|  | .into_iter() | 
|  | .map(|c| c.as_str().unwrap()) | 
|  | .intersperse(", ") | 
|  | .collect(); | 
|  | early_dcx.early_fatal(format!( | 
|  | "some `-C link-self-contained` components were both enabled and disabled: {names}" | 
|  | )); | 
|  | } | 
|  |  | 
|  | let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); | 
|  |  | 
|  | // -Zretpoline-external-thunk also requires -Zretpoline | 
|  | if unstable_opts.retpoline_external_thunk { | 
|  | unstable_opts.retpoline = true; | 
|  | target_modifiers.insert( | 
|  | OptionsTargetModifiers::UnstableOptions(UnstableOptionsTargetModifiers::retpoline), | 
|  | "true".to_string(), | 
|  | ); | 
|  | } | 
|  |  | 
|  | let cg = cg; | 
|  |  | 
|  | let opt_level = parse_opt_level(early_dcx, matches, &cg); | 
|  | // 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); | 
|  | let debuginfo_compression = unstable_opts.debuginfo_compression; | 
|  |  | 
|  | if !unstable_options_enabled { | 
|  | if let Err(error) = cg.linker_features.check_unstable_variants(&target_triple) { | 
|  | early_dcx.early_fatal(error); | 
|  | } | 
|  | } | 
|  |  | 
|  | let crate_name = matches.opt_str("crate-name"); | 
|  | let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref()); | 
|  | // Parse any `-l` flags, which link to native libraries. | 
|  | let libs = parse_native_libs(early_dcx, &unstable_opts, unstable_features, matches); | 
|  |  | 
|  | let test = matches.opt_present("test"); | 
|  |  | 
|  | if !cg.remark.is_empty() && debuginfo == DebugInfo::None { | 
|  | early_dcx.early_warn("-C remark requires \"-C debuginfo=n\" to show source locations"); | 
|  | } | 
|  |  | 
|  | if cg.remark.is_empty() && unstable_opts.remark_dir.is_some() { | 
|  | early_dcx | 
|  | .early_warn("using -Z remark-dir without enabling remarks using e.g. -C remark=all"); | 
|  | } | 
|  |  | 
|  | let externs = parse_externs(early_dcx, matches, &unstable_opts); | 
|  |  | 
|  | let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts); | 
|  |  | 
|  | let pretty = parse_pretty(early_dcx, &unstable_opts); | 
|  |  | 
|  | // query-dep-graph is required if dump-dep-graph is given #106736 | 
|  | if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph { | 
|  | early_dcx.early_fatal("can't dump dependency graph without `-Z query-dep-graph`"); | 
|  | } | 
|  |  | 
|  | let logical_env = parse_logical_env(early_dcx, matches); | 
|  |  | 
|  | let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from)); | 
|  |  | 
|  | let real_source_base_dir = |suffix: &str, confirm: &str| { | 
|  | let mut candidate = sysroot.path().join(suffix); | 
|  | if let Ok(metadata) = candidate.symlink_metadata() { | 
|  | // Replace the symlink bootstrap creates, with its destination. | 
|  | // We could try to use `fs::canonicalize` instead, but that might | 
|  | // produce unnecessarily verbose path. | 
|  | if metadata.file_type().is_symlink() { | 
|  | if let Ok(symlink_dest) = std::fs::read_link(&candidate) { | 
|  | candidate = symlink_dest; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Only use this directory if it has a file we can expect to always find. | 
|  | candidate.join(confirm).is_file().then_some(candidate) | 
|  | }; | 
|  |  | 
|  | let real_rust_source_base_dir = | 
|  | // This is the location used by the `rust-src` `rustup` component. | 
|  | real_source_base_dir("lib/rustlib/src/rust", "library/std/src/lib.rs"); | 
|  |  | 
|  | let real_rustc_dev_source_base_dir = | 
|  | // This is the location used by the `rustc-dev` `rustup` component. | 
|  | real_source_base_dir("lib/rustlib/rustc-src/rust", "compiler/rustc/src/main.rs"); | 
|  |  | 
|  | let mut search_paths = vec![]; | 
|  | for s in &matches.opt_strs("L") { | 
|  | search_paths.push(SearchPath::from_cli_opt( | 
|  | sysroot.path(), | 
|  | &target_triple, | 
|  | early_dcx, | 
|  | s, | 
|  | unstable_opts.unstable_options, | 
|  | )); | 
|  | } | 
|  |  | 
|  | let working_dir = std::env::current_dir().unwrap_or_else(|e| { | 
|  | early_dcx.early_fatal(format!("Current directory is invalid: {e}")); | 
|  | }); | 
|  |  | 
|  | let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts); | 
|  | let working_dir = file_mapping.to_real_filename(&working_dir); | 
|  |  | 
|  | let verbose = matches.opt_present("verbose") || unstable_opts.verbose_internals; | 
|  |  | 
|  | Options { | 
|  | assert_incr_state, | 
|  | crate_types, | 
|  | optimize: opt_level, | 
|  | debuginfo, | 
|  | debuginfo_compression, | 
|  | lint_opts, | 
|  | lint_cap, | 
|  | describe_lints, | 
|  | output_types, | 
|  | search_paths, | 
|  | sysroot, | 
|  | target_triple, | 
|  | test, | 
|  | incremental, | 
|  | untracked_state_hash: Default::default(), | 
|  | unstable_opts, | 
|  | prints, | 
|  | cg, | 
|  | error_format, | 
|  | diagnostic_width, | 
|  | externs, | 
|  | unstable_features, | 
|  | crate_name, | 
|  | libs, | 
|  | debug_assertions, | 
|  | actually_rustdoc: false, | 
|  | resolve_doc_links: ResolveDocLinks::ExportedMetadata, | 
|  | trimmed_def_paths: false, | 
|  | cli_forced_codegen_units: codegen_units, | 
|  | cli_forced_local_thinlto_off: disable_local_thinlto, | 
|  | remap_path_prefix, | 
|  | real_rust_source_base_dir, | 
|  | real_rustc_dev_source_base_dir, | 
|  | edition, | 
|  | json_artifact_notifications, | 
|  | json_timings, | 
|  | json_unused_externs, | 
|  | json_future_incompat, | 
|  | pretty, | 
|  | working_dir, | 
|  | color, | 
|  | logical_env, | 
|  | verbose, | 
|  | target_modifiers, | 
|  | } | 
|  | } | 
|  |  | 
|  | fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> Option<PpMode> { | 
|  | use PpMode::*; | 
|  |  | 
|  | let first = match unstable_opts.unpretty.as_deref()? { | 
|  | "normal" => Source(PpSourceMode::Normal), | 
|  | "identified" => Source(PpSourceMode::Identified), | 
|  | "expanded" => Source(PpSourceMode::Expanded), | 
|  | "expanded,identified" => Source(PpSourceMode::ExpandedIdentified), | 
|  | "expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene), | 
|  | "ast-tree" => AstTree, | 
|  | "ast-tree,expanded" => AstTreeExpanded, | 
|  | "hir" => Hir(PpHirMode::Normal), | 
|  | "hir,identified" => Hir(PpHirMode::Identified), | 
|  | "hir,typed" => Hir(PpHirMode::Typed), | 
|  | "hir-tree" => HirTree, | 
|  | "thir-tree" => ThirTree, | 
|  | "thir-flat" => ThirFlat, | 
|  | "mir" => Mir, | 
|  | "stable-mir" => StableMir, | 
|  | "mir-cfg" => MirCFG, | 
|  | name => early_dcx.early_fatal(format!( | 
|  | "argument to `unpretty` must be one of `normal`, `identified`, \ | 
|  | `expanded`, `expanded,identified`, `expanded,hygiene`, \ | 
|  | `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \ | 
|  | `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir`, `stable-mir`, or \ | 
|  | `mir-cfg`; got {name}" | 
|  | )), | 
|  | }; | 
|  | debug!("got unpretty option: {first:?}"); | 
|  | Some(first) | 
|  | } | 
|  |  | 
|  | pub fn make_crate_type_option() -> RustcOptGroup { | 
|  | make_opt( | 
|  | OptionStability::Stable, | 
|  | OptionKind::Multi, | 
|  | "", | 
|  | "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<String>) -> Result<Vec<CrateType>, String> { | 
|  | let mut crate_types: Vec<CrateType> = 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, | 
|  | "sdylib" => CrateType::Sdylib, | 
|  | _ => { | 
|  | return Err(format!( | 
|  | "unknown crate type: `{part}`, expected one of: \ | 
|  | `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro`", | 
|  | )); | 
|  | } | 
|  | }; | 
|  | if !crate_types.contains(&new_part) { | 
|  | crate_types.push(new_part) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Ok(crate_types) | 
|  | } | 
|  |  | 
|  | pub mod nightly_options { | 
|  | use rustc_feature::UnstableFeatures; | 
|  |  | 
|  | use super::{OptionStability, RustcOptGroup}; | 
|  | use crate::EarlyDiagCtxt; | 
|  |  | 
|  | pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { | 
|  | match_is_nightly_build(matches) | 
|  | && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options") | 
|  | } | 
|  |  | 
|  | pub fn match_is_nightly_build(matches: &getopts::Matches) -> bool { | 
|  | is_nightly_build(matches.opt_str("crate-name").as_deref()) | 
|  | } | 
|  |  | 
|  | fn is_nightly_build(krate: Option<&str>) -> bool { | 
|  | UnstableFeatures::from_environment(krate).is_nightly_build() | 
|  | } | 
|  |  | 
|  | pub fn check_nightly_options( | 
|  | early_dcx: &EarlyDiagCtxt, | 
|  | 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 = match_is_nightly_build(matches); | 
|  | let mut nightly_options_on_stable = 0; | 
|  |  | 
|  | 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_dcx.early_fatal(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 => { | 
|  | nightly_options_on_stable += 1; | 
|  | let msg = format!( | 
|  | "the option `{}` is only accepted on the nightly compiler", | 
|  | opt.name | 
|  | ); | 
|  | // The non-zero nightly_options_on_stable will force an early_fatal eventually. | 
|  | let _ = early_dcx.early_err(msg); | 
|  | } | 
|  | OptionStability::Stable => {} | 
|  | } | 
|  | } | 
|  | if nightly_options_on_stable > 0 { | 
|  | early_dcx | 
|  | .early_help("consider switching to a nightly toolchain: `rustup default nightly`"); | 
|  | early_dcx.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>"); | 
|  | early_dcx.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>"); | 
|  | early_dcx.early_fatal(format!( | 
|  | "{} nightly option{} were parsed", | 
|  | nightly_options_on_stable, | 
|  | if nightly_options_on_stable > 1 { "s" } else { "" } | 
|  | )); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | 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), | 
|  | CrateType::Sdylib => "sdylib".fmt(f), | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl IntoDiagArg for CrateType { | 
|  | fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue { | 
|  | self.to_string().into_diag_arg(&mut None) | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Copy, Clone, PartialEq, Debug)] | 
|  | pub enum PpSourceMode { | 
|  | /// `-Zunpretty=normal` | 
|  | Normal, | 
|  | /// `-Zunpretty=expanded` | 
|  | Expanded, | 
|  | /// `-Zunpretty=identified` | 
|  | Identified, | 
|  | /// `-Zunpretty=expanded,identified` | 
|  | ExpandedIdentified, | 
|  | /// `-Zunpretty=expanded,hygiene` | 
|  | ExpandedHygiene, | 
|  | } | 
|  |  | 
|  | #[derive(Copy, Clone, PartialEq, Debug)] | 
|  | pub enum PpHirMode { | 
|  | /// `-Zunpretty=hir` | 
|  | Normal, | 
|  | /// `-Zunpretty=hir,identified` | 
|  | Identified, | 
|  | /// `-Zunpretty=hir,typed` | 
|  | Typed, | 
|  | } | 
|  |  | 
|  | #[derive(Copy, Clone, PartialEq, Debug)] | 
|  | /// Pretty print mode | 
|  | pub enum PpMode { | 
|  | /// Options that print the source code, i.e. | 
|  | /// `-Zunpretty=normal` and `-Zunpretty=expanded` | 
|  | Source(PpSourceMode), | 
|  | /// `-Zunpretty=ast-tree` | 
|  | AstTree, | 
|  | /// `-Zunpretty=ast-tree,expanded` | 
|  | AstTreeExpanded, | 
|  | /// Options that print the HIR, i.e. `-Zunpretty=hir` | 
|  | Hir(PpHirMode), | 
|  | /// `-Zunpretty=hir-tree` | 
|  | HirTree, | 
|  | /// `-Zunpretty=thir-tree` | 
|  | ThirTree, | 
|  | /// `-Zunpretty=thir-flat` | 
|  | ThirFlat, | 
|  | /// `-Zunpretty=mir` | 
|  | Mir, | 
|  | /// `-Zunpretty=mir-cfg` | 
|  | MirCFG, | 
|  | /// `-Zunpretty=stable-mir` | 
|  | StableMir, | 
|  | } | 
|  |  | 
|  | impl PpMode { | 
|  | pub fn needs_ast_map(&self) -> bool { | 
|  | use PpMode::*; | 
|  | use PpSourceMode::*; | 
|  | match *self { | 
|  | Source(Normal | Identified) | AstTree => false, | 
|  |  | 
|  | Source(Expanded | ExpandedIdentified | ExpandedHygiene) | 
|  | | AstTreeExpanded | 
|  | | Hir(_) | 
|  | | HirTree | 
|  | | ThirTree | 
|  | | ThirFlat | 
|  | | Mir | 
|  | | MirCFG | 
|  | | StableMir => true, | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn needs_analysis(&self) -> bool { | 
|  | use PpMode::*; | 
|  | matches!(*self, Hir(PpHirMode::Typed) | Mir | StableMir | MirCFG | ThirTree | ThirFlat) | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Hash, PartialEq, Eq, Debug)] | 
|  | pub enum WasiExecModel { | 
|  | Command, | 
|  | Reactor, | 
|  | } | 
|  |  | 
|  | /// 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. | 
|  | pub(crate) mod dep_tracking { | 
|  | use std::collections::BTreeMap; | 
|  | use std::hash::Hash; | 
|  | use std::num::NonZero; | 
|  | use std::path::PathBuf; | 
|  |  | 
|  | use rustc_abi::Align; | 
|  | use rustc_data_structures::fx::FxIndexMap; | 
|  | use rustc_data_structures::stable_hasher::StableHasher; | 
|  | use rustc_errors::LanguageIdentifier; | 
|  | use rustc_feature::UnstableFeatures; | 
|  | use rustc_hashes::Hash64; | 
|  | use rustc_span::RealFileName; | 
|  | use rustc_span::edition::Edition; | 
|  | use rustc_target::spec::{ | 
|  | CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, | 
|  | RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple, | 
|  | TlsModel, | 
|  | }; | 
|  |  | 
|  | use super::{ | 
|  | AutoDiff, BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions, | 
|  | CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, | 
|  | InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, | 
|  | LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OomStrategy, OptLevel, OutFileName, | 
|  | OutputType, OutputTypes, PatchableFunctionEntry, Polonius, RemapPathScopeComponents, | 
|  | ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, | 
|  | SymbolManglingVersion, WasiExecModel, | 
|  | }; | 
|  | use crate::lint; | 
|  | use crate::utils::NativeLib; | 
|  |  | 
|  | pub(crate) trait DepTrackingHash { | 
|  | fn hash( | 
|  | &self, | 
|  | hasher: &mut StableHasher, | 
|  | error_format: ErrorOutputType, | 
|  | for_crate_hash: bool, | 
|  | ); | 
|  | } | 
|  |  | 
|  | macro_rules! impl_dep_tracking_hash_via_hash { | 
|  | ($($t:ty),+ $(,)?) => {$( | 
|  | impl DepTrackingHash for $t { | 
|  | fn hash(&self, hasher: &mut StableHasher, _: ErrorOutputType, _for_crate_hash: bool) { | 
|  | Hash::hash(self, hasher); | 
|  | } | 
|  | } | 
|  | )+}; | 
|  | } | 
|  |  | 
|  | impl<T: DepTrackingHash> DepTrackingHash for Option<T> { | 
|  | fn hash( | 
|  | &self, | 
|  | hasher: &mut StableHasher, | 
|  | error_format: ErrorOutputType, | 
|  | for_crate_hash: bool, | 
|  | ) { | 
|  | match self { | 
|  | Some(x) => { | 
|  | Hash::hash(&1, hasher); | 
|  | DepTrackingHash::hash(x, hasher, error_format, for_crate_hash); | 
|  | } | 
|  | None => Hash::hash(&0, hasher), | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl_dep_tracking_hash_via_hash!( | 
|  | (), | 
|  | AutoDiff, | 
|  | Offload, | 
|  | bool, | 
|  | usize, | 
|  | NonZero<usize>, | 
|  | u64, | 
|  | Hash64, | 
|  | String, | 
|  | PathBuf, | 
|  | lint::Level, | 
|  | WasiExecModel, | 
|  | u32, | 
|  | FramePointer, | 
|  | RelocModel, | 
|  | CodeModel, | 
|  | TlsModel, | 
|  | InstrumentCoverage, | 
|  | CoverageOptions, | 
|  | InstrumentXRay, | 
|  | CrateType, | 
|  | MergeFunctions, | 
|  | OnBrokenPipe, | 
|  | PanicStrategy, | 
|  | RelroLevel, | 
|  | OptLevel, | 
|  | LtoCli, | 
|  | DebugInfo, | 
|  | DebugInfoCompression, | 
|  | MirStripDebugInfo, | 
|  | CollapseMacroDebuginfo, | 
|  | UnstableFeatures, | 
|  | NativeLib, | 
|  | SanitizerSet, | 
|  | CFGuard, | 
|  | CFProtection, | 
|  | TargetTuple, | 
|  | Edition, | 
|  | LinkerPluginLto, | 
|  | ResolveDocLinks, | 
|  | SplitDebuginfo, | 
|  | SplitDwarfKind, | 
|  | StackProtector, | 
|  | SwitchWithOptPath, | 
|  | SymbolManglingVersion, | 
|  | SymbolVisibility, | 
|  | RemapPathScopeComponents, | 
|  | SourceFileHashAlgorithm, | 
|  | OutFileName, | 
|  | OutputType, | 
|  | RealFileName, | 
|  | LocationDetail, | 
|  | FmtDebug, | 
|  | BranchProtection, | 
|  | OomStrategy, | 
|  | LanguageIdentifier, | 
|  | NextSolverConfig, | 
|  | PatchableFunctionEntry, | 
|  | Polonius, | 
|  | InliningThreshold, | 
|  | FunctionReturn, | 
|  | Align, | 
|  | ); | 
|  |  | 
|  | impl<T1, T2> DepTrackingHash for (T1, T2) | 
|  | where | 
|  | T1: DepTrackingHash, | 
|  | T2: DepTrackingHash, | 
|  | { | 
|  | fn hash( | 
|  | &self, | 
|  | hasher: &mut StableHasher, | 
|  | error_format: ErrorOutputType, | 
|  | for_crate_hash: bool, | 
|  | ) { | 
|  | Hash::hash(&0, hasher); | 
|  | DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash); | 
|  | Hash::hash(&1, hasher); | 
|  | DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash); | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<T1, T2, T3> DepTrackingHash for (T1, T2, T3) | 
|  | where | 
|  | T1: DepTrackingHash, | 
|  | T2: DepTrackingHash, | 
|  | T3: DepTrackingHash, | 
|  | { | 
|  | fn hash( | 
|  | &self, | 
|  | hasher: &mut StableHasher, | 
|  | error_format: ErrorOutputType, | 
|  | for_crate_hash: bool, | 
|  | ) { | 
|  | Hash::hash(&0, hasher); | 
|  | DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash); | 
|  | Hash::hash(&1, hasher); | 
|  | DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash); | 
|  | Hash::hash(&2, hasher); | 
|  | DepTrackingHash::hash(&self.2, hasher, error_format, for_crate_hash); | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<T: DepTrackingHash> DepTrackingHash for Vec<T> { | 
|  | fn hash( | 
|  | &self, | 
|  | hasher: &mut StableHasher, | 
|  | error_format: ErrorOutputType, | 
|  | for_crate_hash: bool, | 
|  | ) { | 
|  | Hash::hash(&self.len(), hasher); | 
|  | for (index, elem) in self.iter().enumerate() { | 
|  | Hash::hash(&index, hasher); | 
|  | DepTrackingHash::hash(elem, hasher, error_format, for_crate_hash); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<T: DepTrackingHash, V: DepTrackingHash> DepTrackingHash for FxIndexMap<T, V> { | 
|  | fn hash( | 
|  | &self, | 
|  | hasher: &mut StableHasher, | 
|  | error_format: ErrorOutputType, | 
|  | for_crate_hash: bool, | 
|  | ) { | 
|  | Hash::hash(&self.len(), hasher); | 
|  | for (key, value) in self.iter() { | 
|  | DepTrackingHash::hash(key, hasher, error_format, for_crate_hash); | 
|  | DepTrackingHash::hash(value, hasher, error_format, for_crate_hash); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl DepTrackingHash for OutputTypes { | 
|  | fn hash( | 
|  | &self, | 
|  | hasher: &mut StableHasher, | 
|  | error_format: ErrorOutputType, | 
|  | for_crate_hash: bool, | 
|  | ) { | 
|  | Hash::hash(&self.0.len(), hasher); | 
|  | for (key, val) in &self.0 { | 
|  | DepTrackingHash::hash(key, hasher, error_format, for_crate_hash); | 
|  | if !for_crate_hash { | 
|  | DepTrackingHash::hash(val, hasher, error_format, for_crate_hash); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // This is a stable hash because BTreeMap is a sorted container | 
|  | pub(crate) fn stable_hash( | 
|  | sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, | 
|  | hasher: &mut StableHasher, | 
|  | error_format: ErrorOutputType, | 
|  | for_crate_hash: bool, | 
|  | ) { | 
|  | 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, for_crate_hash); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Default behavior to use in out-of-memory situations. | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug, Encodable, Decodable, HashStable_Generic)] | 
|  | pub enum OomStrategy { | 
|  | /// Generate a panic that can be caught by `catch_unwind`. | 
|  | Panic, | 
|  |  | 
|  | /// Abort the process immediately. | 
|  | Abort, | 
|  | } | 
|  |  | 
|  | impl OomStrategy { | 
|  | pub const SYMBOL: &'static str = "__rust_alloc_error_handler_should_panic_v2"; | 
|  |  | 
|  | pub fn should_panic(self) -> u8 { | 
|  | match self { | 
|  | OomStrategy::Panic => 1, | 
|  | OomStrategy::Abort => 0, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// How to run proc-macro code when building this crate | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | 
|  | pub enum ProcMacroExecutionStrategy { | 
|  | /// Run the proc-macro code on the same thread as the server. | 
|  | SameThread, | 
|  |  | 
|  | /// Run the proc-macro code on a different thread. | 
|  | CrossThread, | 
|  | } | 
|  |  | 
|  | /// How to perform collapse macros debug info | 
|  | /// if-ext - if macro from different crate (related to callsite code) | 
|  | /// | cmd \ attr    | no  | (unspecified) | external | yes | | 
|  | /// | no            | no  | no            | no       | no  | | 
|  | /// | (unspecified) | no  | no            | if-ext   | yes | | 
|  | /// | external      | no  | if-ext        | if-ext   | yes | | 
|  | /// | yes           | yes | yes           | yes      | yes | | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | 
|  | pub enum CollapseMacroDebuginfo { | 
|  | /// Don't collapse debuginfo for the macro | 
|  | No = 0, | 
|  | /// Unspecified value | 
|  | Unspecified = 1, | 
|  | /// Collapse debuginfo if the macro comes from a different crate | 
|  | External = 2, | 
|  | /// Collapse debuginfo for the macro | 
|  | Yes = 3, | 
|  | } | 
|  |  | 
|  | /// Which format to use for `-Z dump-mono-stats` | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | 
|  | pub enum DumpMonoStatsFormat { | 
|  | /// Pretty-print a markdown table | 
|  | Markdown, | 
|  | /// Emit structured JSON | 
|  | Json, | 
|  | } | 
|  |  | 
|  | impl DumpMonoStatsFormat { | 
|  | pub fn extension(self) -> &'static str { | 
|  | match self { | 
|  | Self::Markdown => "md", | 
|  | Self::Json => "json", | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// `-Z patchable-function-entry` representation - how many nops to put before and after function | 
|  | /// entry. | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] | 
|  | pub struct PatchableFunctionEntry { | 
|  | /// Nops before the entry | 
|  | prefix: u8, | 
|  | /// Nops after the entry | 
|  | entry: u8, | 
|  | } | 
|  |  | 
|  | impl PatchableFunctionEntry { | 
|  | pub fn from_total_and_prefix_nops( | 
|  | total_nops: u8, | 
|  | prefix_nops: u8, | 
|  | ) -> Option<PatchableFunctionEntry> { | 
|  | if total_nops < prefix_nops { | 
|  | None | 
|  | } else { | 
|  | Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops }) | 
|  | } | 
|  | } | 
|  | pub fn prefix(&self) -> u8 { | 
|  | self.prefix | 
|  | } | 
|  | pub fn entry(&self) -> u8 { | 
|  | self.entry | 
|  | } | 
|  | } | 
|  |  | 
|  | /// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy, | 
|  | /// or future prototype. | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] | 
|  | pub enum Polonius { | 
|  | /// The default value: disabled. | 
|  | #[default] | 
|  | Off, | 
|  |  | 
|  | /// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`. | 
|  | Legacy, | 
|  |  | 
|  | /// In-tree prototype, extending the NLL infrastructure. | 
|  | Next, | 
|  | } | 
|  |  | 
|  | impl Polonius { | 
|  | /// Returns whether the legacy version of polonius is enabled | 
|  | pub fn is_legacy_enabled(&self) -> bool { | 
|  | matches!(self, Polonius::Legacy) | 
|  | } | 
|  |  | 
|  | /// Returns whether the "next" version of polonius is enabled | 
|  | pub fn is_next_enabled(&self) -> bool { | 
|  | matches!(self, Polonius::Next) | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | 
|  | pub enum InliningThreshold { | 
|  | Always, | 
|  | Sometimes(usize), | 
|  | Never, | 
|  | } | 
|  |  | 
|  | impl Default for InliningThreshold { | 
|  | fn default() -> Self { | 
|  | Self::Sometimes(100) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// The different settings that the `-Zfunction-return` flag can have. | 
|  | #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] | 
|  | pub enum FunctionReturn { | 
|  | /// Keep the function return unmodified. | 
|  | #[default] | 
|  | Keep, | 
|  |  | 
|  | /// Replace returns with jumps to thunk, without emitting the thunk. | 
|  | ThunkExtern, | 
|  | } | 
|  |  | 
|  | /// Whether extra span comments are included when dumping MIR, via the `-Z mir-include-spans` flag. | 
|  | /// By default, only enabled in the NLL MIR dumps, and disabled in all other passes. | 
|  | #[derive(Clone, Copy, Default, PartialEq, Debug)] | 
|  | pub enum MirIncludeSpans { | 
|  | Off, | 
|  | On, | 
|  | /// Default: include extra comments in NLL MIR dumps only. Can be ignored and considered as | 
|  | /// `Off` in all other cases. | 
|  | #[default] | 
|  | Nll, | 
|  | } | 
|  |  | 
|  | impl MirIncludeSpans { | 
|  | /// Unless opting into extra comments for all passes, they can be considered disabled. | 
|  | /// The cases where a distinction between on/off and a per-pass value can exist will be handled | 
|  | /// in the passes themselves: i.e. the `Nll` value is considered off for all intents and | 
|  | /// purposes, except for the NLL MIR dump pass. | 
|  | pub fn is_enabled(self) -> bool { | 
|  | self == MirIncludeSpans::On | 
|  | } | 
|  | } |