| //! Contains infrastructure for configuring the compiler, including parsing | |
| //! command-line options. | |
| 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, fs, iter}; | |
| use externs::{ExternOpt, split_extern_opt}; | |
| use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; | |
| use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey}; | |
| use rustc_errors::emitter::HumanReadableErrorType; | |
| use rustc_errors::{ColorConfig, DiagCtxtFlags}; | |
| use rustc_feature::UnstableFeatures; | |
| use rustc_hashes::Hash64; | |
| use rustc_macros::{BlobDecodable, 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, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, Symbol, sym, | |
| }; | |
| use rustc_target::spec::{ | |
| FramePointer, LinkSelfContainedComponents, LinkerFeatures, PanicStrategy, SplitDebuginfo, | |
| Target, TargetTuple, | |
| }; | |
| use tracing::debug; | |
| pub use crate::config::cfg::{Cfg, CheckCfg, ExpectedValues}; | |
| use crate::config::native_libs::parse_native_libs; | |
| pub use crate::config::print_request::{PrintKind, PrintRequest}; | |
| 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; | |
| mod print_request; | |
| pub mod sigpipe; | |
| /// 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, Encodable, Decodable)] | |
| 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, Encodable, Decodable)] | |
| 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, | |
| /// **(internal test-only flag)** | |
| /// `-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 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, | |
| } | |
| // The different settings that the `-Z offload` flag can have. | |
| #[derive(Clone, PartialEq, Hash, Debug, Encodable, Decodable)] | |
| pub enum Offload { | |
| /// Entry point for `std::offload`, enables kernel compilation for a gpu device | |
| Device, | |
| /// Second step in the offload pipeline, generates the host code to call kernels. | |
| Host(String), | |
| /// Test is similar to Host, but allows testing without a device artifact. | |
| Test, | |
| } | |
| /// The different settings that the `-Z autodiff` flag can have. | |
| #[derive(Clone, PartialEq, Hash, Debug, Encodable, Decodable)] | |
| 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, | |
| /// Disable Type Tree | |
| NoTT, | |
| } | |
| /// The different settings that the `-Z annotate-moves` flag can have. | |
| #[derive(Clone, Copy, PartialEq, Hash, Debug)] | |
| pub enum AnnotateMoves { | |
| /// `-Z annotate-moves=no` (or `off`, `false` etc.) | |
| Disabled, | |
| /// `-Z annotate-moves` or `-Z annotate-moves=yes` (use default size limit) | |
| /// `-Z annotate-moves=SIZE` (use specified size limit) | |
| Enabled(Option<u64>), | |
| } | |
| /// 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).ok()?); | |
| 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).ok()?); | |
| 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, Encodable, Decodable)] | |
| 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, BlobDecodable)] | |
| 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, | |
| } | |
| #[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, Encodable, Decodable)] | |
| 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 { short: false, unicode: false }, | |
| 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(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 file_name(&self, session: &Session) -> FileName { | |
| match *self { | |
| Input::File(ref ifile) => FileName::Real( | |
| session | |
| .psess | |
| .source_map() | |
| .path_mapping() | |
| .to_real_filename(session.psess.source_map().working_dir(), ifile.as_path()), | |
| ), | |
| 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, Eq, 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>, | |
| explicit_dwo_out_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"; | |
| pub const MAX_FILENAME_LENGTH: usize = 143; // ecryptfs limits filenames to 143 bytes see #49914 | |
| /// Ensure the filename is not too long, as some filesystems have a limit. | |
| /// If the filename is too long, hash part of it and append the hash to the filename. | |
| /// This is a workaround for long crate names generating overly long filenames. | |
| fn maybe_strip_file_name(mut path: PathBuf) -> PathBuf { | |
| if path.file_name().map_or(0, |name| name.len()) > MAX_FILENAME_LENGTH { | |
| let filename = path.file_name().unwrap().to_string_lossy(); | |
| let hash_len = 64 / 4; // Hash64 is 64 bits encoded in hex | |
| let hyphen_len = 1; // the '-' we insert between hash and suffix | |
| // number of bytes of suffix we can keep so that "hash-<suffix>" fits | |
| let allowed_suffix = MAX_FILENAME_LENGTH.saturating_sub(hash_len + hyphen_len); | |
| // number of bytes to remove from the start | |
| let stripped_bytes = filename.len().saturating_sub(allowed_suffix); | |
| // ensure we don't cut in a middle of a char | |
| let split_at = filename.ceil_char_boundary(stripped_bytes); | |
| let mut hasher = StableHasher::new(); | |
| filename[..split_at].hash(&mut hasher); | |
| let hash = hasher.finish::<Hash64>(); | |
| path.set_file_name(format!("{:x}-{}", hash, &filename[split_at..])); | |
| } | |
| path | |
| } | |
| impl OutputFilenames { | |
| pub fn new( | |
| out_directory: PathBuf, | |
| out_crate_name: String, | |
| out_filestem: String, | |
| single_output_file: Option<OutFileName>, | |
| temps_directory: Option<PathBuf>, | |
| explicit_dwo_out_directory: Option<PathBuf>, | |
| extra: String, | |
| outputs: OutputTypes, | |
| ) -> Self { | |
| OutputFilenames { | |
| out_directory, | |
| single_output_file, | |
| temps_directory, | |
| explicit_dwo_out_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 { | |
| let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp); | |
| if let Some(dwo_out) = &self.explicit_dwo_out_directory { | |
| let mut o = dwo_out.clone(); | |
| o.push(p.file_name().unwrap()); | |
| o | |
| } else { | |
| p | |
| } | |
| } | |
| /// 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); | |
| maybe_strip_file_name(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) | |
| } | |
| } | |
| } | |
| } | |
| pub(crate) fn parse_remap_path_scope( | |
| early_dcx: &EarlyDiagCtxt, | |
| matches: &getopts::Matches, | |
| unstable_opts: &UnstableOptions, | |
| ) -> RemapPathScopeComponents { | |
| if let Some(v) = matches.opt_str("remap-path-scope") { | |
| let mut slot = RemapPathScopeComponents::empty(); | |
| for s in v.split(',') { | |
| slot |= match s { | |
| "macro" => RemapPathScopeComponents::MACRO, | |
| "diagnostics" => RemapPathScopeComponents::DIAGNOSTICS, | |
| "documentation" => { | |
| if !unstable_opts.unstable_options { | |
| early_dcx.early_fatal("remapping `documentation` path scope requested but `-Zunstable-options` not specified"); | |
| } | |
| RemapPathScopeComponents::DOCUMENTATION | |
| }, | |
| "debuginfo" => RemapPathScopeComponents::DEBUGINFO, | |
| "coverage" => RemapPathScopeComponents::COVERAGE, | |
| "object" => RemapPathScopeComponents::OBJECT, | |
| "all" => RemapPathScopeComponents::all(), | |
| _ => early_dcx.early_fatal("argument for `--remap-path-scope` must be a comma separated list of scopes: `macro`, `diagnostics`, `documentation`, `debuginfo`, `coverage`, `object`, `all`"), | |
| } | |
| } | |
| slot | |
| } else { | |
| RemapPathScopeComponents::all() | |
| } | |
| } | |
| #[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)>, | |
| remap_path_scope: RemapPathScopeComponents, | |
| ) -> FilePathMapping { | |
| FilePathMapping::new(remap_path_prefix.clone(), remap_path_scope) | |
| } | |
| impl Default for Options { | |
| fn default() -> Options { | |
| let unstable_opts = UnstableOptions::default(); | |
| // FIXME(Urgau): This is a hack that ideally shouldn't exist, but rustdoc | |
| // currently uses this `Default` implementation, so we have no choice but | |
| // to create a default working directory. | |
| let working_dir = { | |
| let working_dir = std::env::current_dir().unwrap(); | |
| let file_mapping = file_path_mapping(Vec::new(), RemapPathScopeComponents::empty()); | |
| file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) | |
| }; | |
| Options { | |
| assert_incr_state: None, | |
| crate_types: Vec::new(), | |
| optimize: OptLevel::No, | |
| debuginfo: DebugInfo::None, | |
| lint_opts: Vec::new(), | |
| lint_cap: None, | |
| describe_lints: false, | |
| output_types: OutputTypes(BTreeMap::new()), | |
| search_paths: vec![], | |
| sysroot: Sysroot::new(None), | |
| target_triple: TargetTuple::from_tuple(host_tuple()), | |
| test: false, | |
| incremental: None, | |
| untracked_state_hash: Default::default(), | |
| unstable_opts, | |
| 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(), | |
| remap_path_scope: RemapPathScopeComponents::all(), | |
| 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, | |
| 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.remap_path_scope) | |
| } | |
| /// 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(if self.unstable_features.is_nightly_build() { | |
| SymbolManglingVersion::V0 | |
| } else { | |
| SymbolManglingVersion::Legacy | |
| }) | |
| } | |
| #[inline] | |
| pub fn autodiff_enabled(&self) -> bool { | |
| self.unstable_opts.autodiff.contains(&AutoDiff::Enable) | |
| } | |
| } | |
| 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, | |
| }, | |
| } | |
| pub use rustc_hir::attrs::CrateType; | |
| #[derive(Clone, Hash, Debug, PartialEq, Eq, Encodable, Decodable)] | |
| 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 gcs: bool, | |
| } | |
| 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, | |
| unstable_options: bool, | |
| ) -> Target { | |
| match Target::search(target, sysroot, unstable_options) { | |
| 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 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_request::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 tuple 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( | |
| Stable, | |
| Opt, | |
| "", | |
| "remap-path-scope", | |
| "Defines which scopes of paths should be remapped by `--remap-path-prefix`", | |
| "<macro,diagnostics,debuginfo,coverage,object,all>", | |
| ), | |
| 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 { short: false, unicode: false }; | |
| 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: true, unicode: false }; | |
| } | |
| "diagnostic-unicode" => { | |
| json_rendered = HumanReadableErrorType { short: false, unicode: true }; | |
| } | |
| "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 { | |
| let default_kind = HumanReadableErrorType { short: false, unicode: false }; | |
| // 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, kind: default_kind } | |
| } | |
| 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: true, unicode: false }, | |
| color_config, | |
| }, | |
| Some("human-unicode") => ErrorOutputType::HumanReadable { | |
| kind: HumanReadableErrorType { short: false, unicode: true }, | |
| color_config, | |
| }, | |
| Some(arg) => { | |
| early_dcx.set_error_format(ErrorOutputType::HumanReadable { | |
| color_config, | |
| kind: default_kind, | |
| }); | |
| early_dcx.early_fatal(format!( | |
| "argument for `--error-format` must be `human`, `human-unicode`, \ | |
| `json`, `pretty-json` or `short` (instead was `{arg}`)" | |
| )) | |
| } | |
| } | |
| } else { | |
| ErrorOutputType::HumanReadable { color_config, kind: default_kind } | |
| }; | |
| 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, | |
| is_nightly_build: bool, | |
| format: ErrorOutputType, | |
| ) { | |
| if unstable_opts.unstable_options || is_nightly_build { | |
| return; | |
| } | |
| let format = match format { | |
| ErrorOutputType::Json { pretty: true, .. } => "pretty-json", | |
| ErrorOutputType::HumanReadable { kind, .. } => match kind { | |
| HumanReadableErrorType { unicode: true, .. } => "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) | |
| } | |
| 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 crate_name = matches.opt_str("crate-name"); | |
| let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref()); | |
| 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, | |
| unstable_features.is_nightly_build(), | |
| 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.is_empty() { | |
| 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 = print_request::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); | |
| if !unstable_options_enabled { | |
| if let Err(error) = cg.linker_features.check_unstable_variants(&target_triple) { | |
| early_dcx.early_fatal(error); | |
| } | |
| } | |
| if !unstable_options_enabled && cg.panic == Some(PanicStrategy::ImmediateAbort) { | |
| early_dcx.early_fatal( | |
| "`-Cpanic=immediate-abort` requires `-Zunstable-options` and a nightly compiler", | |
| ) | |
| } | |
| // 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 remap_path_scope = parse_remap_path_scope(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"); | |
| // We eagerly scan all files in each passed -L path. If the same directory is passed multiple | |
| // times, and the directory contains a lot of files, this can take a lot of time. | |
| // So we remove -L paths that were passed multiple times, and keep only the first occurrence. | |
| // We still have to keep the original order of the -L arguments. | |
| let search_paths: Vec<SearchPath> = { | |
| let mut seen_search_paths = FxHashSet::default(); | |
| let search_path_matches: Vec<String> = matches.opt_strs("L"); | |
| search_path_matches | |
| .iter() | |
| .filter(|p| seen_search_paths.insert(*p)) | |
| .map(|path| { | |
| SearchPath::from_cli_opt( | |
| sysroot.path(), | |
| &target_triple, | |
| early_dcx, | |
| &path, | |
| unstable_opts.unstable_options, | |
| ) | |
| }) | |
| .collect() | |
| }; | |
| // Ideally we would use `SourceMap::working_dir` instead, but we don't have access to it | |
| // so we manually create the potentially-remapped working directory | |
| let working_dir = { | |
| 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(), remap_path_scope); | |
| file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) | |
| }; | |
| let verbose = matches.opt_present("verbose") || unstable_opts.verbose_internals; | |
| Options { | |
| assert_incr_state, | |
| crate_types, | |
| optimize: opt_level, | |
| debuginfo, | |
| 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, | |
| remap_path_scope, | |
| 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" => CrateType::default(), | |
| "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 { "" } | |
| )); | |
| } | |
| } | |
| } | |
| #[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_hir::attrs::CollapseMacroDebuginfo; | |
| use rustc_span::edition::Edition; | |
| use rustc_span::{RealFileName, RemapPathScopeComponents}; | |
| use rustc_target::spec::{ | |
| CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, | |
| RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple, | |
| TlsModel, | |
| }; | |
| use super::{ | |
| AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CoverageOptions, | |
| CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, | |
| InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, | |
| LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, OutFileName, OutputType, | |
| OutputTypes, PatchableFunctionEntry, Polonius, 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!( | |
| (), | |
| AnnotateMoves, | |
| 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, | |
| 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); | |
| } | |
| } | |
| } | |
| /// 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, | |
| } | |
| /// 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 | |
| } | |
| } |