summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_session
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /compiler/rustc_session
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_session')
-rw-r--r--compiler/rustc_session/Cargo.toml20
-rw-r--r--compiler/rustc_session/src/cgu_reuse_tracker.rs118
-rw-r--r--compiler/rustc_session/src/code_stats.rs182
-rw-r--r--compiler/rustc_session/src/config.rs2970
-rw-r--r--compiler/rustc_session/src/cstore.rs218
-rw-r--r--compiler/rustc_session/src/filesearch.rs125
-rw-r--r--compiler/rustc_session/src/lib.rs40
-rw-r--r--compiler/rustc_session/src/options.rs1673
-rw-r--r--compiler/rustc_session/src/output.rs202
-rw-r--r--compiler/rustc_session/src/parse.rs326
-rw-r--r--compiler/rustc_session/src/search_paths.rs93
-rw-r--r--compiler/rustc_session/src/session.rs1599
-rw-r--r--compiler/rustc_session/src/utils.rs93
13 files changed, 7659 insertions, 0 deletions
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
new file mode 100644
index 000000000..37cfc4a0d
--- /dev/null
+++ b/compiler/rustc_session/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "rustc_session"
+version = "0.0.0"
+edition = "2021"
+
+[dependencies]
+getopts = "0.2"
+rustc_macros = { path = "../rustc_macros" }
+tracing = "0.1"
+rustc_errors = { path = "../rustc_errors" }
+rustc_feature = { path = "../rustc_feature" }
+rustc_hir = { path = "../rustc_hir" }
+rustc_target = { path = "../rustc_target" }
+rustc_serialize = { path = "../rustc_serialize" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_span = { path = "../rustc_span" }
+rustc_fs_util = { path = "../rustc_fs_util" }
+num_cpus = "1.0"
+rustc_ast = { path = "../rustc_ast" }
+rustc_lint_defs = { path = "../rustc_lint_defs" }
diff --git a/compiler/rustc_session/src/cgu_reuse_tracker.rs b/compiler/rustc_session/src/cgu_reuse_tracker.rs
new file mode 100644
index 000000000..dd64e8ab7
--- /dev/null
+++ b/compiler/rustc_session/src/cgu_reuse_tracker.rs
@@ -0,0 +1,118 @@
+//! Some facilities for tracking how codegen-units are reused during incremental
+//! compilation. This is used for incremental compilation tests and debug
+//! output.
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_span::{Span, Symbol};
+use std::sync::{Arc, Mutex};
+use tracing::debug;
+
+#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
+pub enum CguReuse {
+ No,
+ PreLto,
+ PostLto,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum ComparisonKind {
+ Exact,
+ AtLeast,
+}
+
+struct TrackerData {
+ actual_reuse: FxHashMap<String, CguReuse>,
+ expected_reuse: FxHashMap<String, (String, SendSpan, CguReuse, ComparisonKind)>,
+}
+
+// Span does not implement `Send`, so we can't just store it in the shared
+// `TrackerData` object. Instead of splitting up `TrackerData` into shared and
+// non-shared parts (which would be complicated), we just mark the `Span` here
+// explicitly as `Send`. That's safe because the span data here is only ever
+// accessed from the main thread.
+struct SendSpan(Span);
+unsafe impl Send for SendSpan {}
+
+#[derive(Clone)]
+pub struct CguReuseTracker {
+ data: Option<Arc<Mutex<TrackerData>>>,
+}
+
+impl CguReuseTracker {
+ pub fn new() -> CguReuseTracker {
+ let data =
+ TrackerData { actual_reuse: Default::default(), expected_reuse: Default::default() };
+
+ CguReuseTracker { data: Some(Arc::new(Mutex::new(data))) }
+ }
+
+ pub fn new_disabled() -> CguReuseTracker {
+ CguReuseTracker { data: None }
+ }
+
+ pub fn set_actual_reuse(&self, cgu_name: &str, kind: CguReuse) {
+ if let Some(ref data) = self.data {
+ debug!("set_actual_reuse({cgu_name:?}, {kind:?})");
+
+ let prev_reuse = data.lock().unwrap().actual_reuse.insert(cgu_name.to_string(), kind);
+
+ if let Some(prev_reuse) = prev_reuse {
+ // The only time it is legal to overwrite reuse state is when
+ // we discover during ThinLTO that we can actually reuse the
+ // post-LTO version of a CGU.
+ assert_eq!(prev_reuse, CguReuse::PreLto);
+ }
+ }
+ }
+
+ pub fn set_expectation(
+ &self,
+ cgu_name: Symbol,
+ cgu_user_name: &str,
+ error_span: Span,
+ expected_reuse: CguReuse,
+ comparison_kind: ComparisonKind,
+ ) {
+ if let Some(ref data) = self.data {
+ debug!("set_expectation({cgu_name:?}, {expected_reuse:?}, {comparison_kind:?})");
+ let mut data = data.lock().unwrap();
+
+ data.expected_reuse.insert(
+ cgu_name.to_string(),
+ (cgu_user_name.to_string(), SendSpan(error_span), expected_reuse, comparison_kind),
+ );
+ }
+ }
+
+ pub fn check_expected_reuse(&self, diag: &rustc_errors::Handler) {
+ if let Some(ref data) = self.data {
+ let data = data.lock().unwrap();
+
+ for (cgu_name, &(ref cgu_user_name, ref error_span, expected_reuse, comparison_kind)) in
+ &data.expected_reuse
+ {
+ if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) {
+ let (error, at_least) = match comparison_kind {
+ ComparisonKind::Exact => (expected_reuse != actual_reuse, false),
+ ComparisonKind::AtLeast => (actual_reuse < expected_reuse, true),
+ };
+
+ if error {
+ let at_least = if at_least { "at least " } else { "" };
+ let msg = format!(
+ "CGU-reuse for `{cgu_user_name}` is `{actual_reuse:?}` but \
+ should be {at_least}`{expected_reuse:?}`"
+ );
+ diag.span_err(error_span.0, &msg);
+ }
+ } else {
+ let msg = format!(
+ "CGU-reuse for `{cgu_user_name}` (mangled: `{cgu_name}`) was \
+ not recorded"
+ );
+ diag.span_fatal(error_span.0, &msg)
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs
new file mode 100644
index 000000000..eede4d16e
--- /dev/null
+++ b/compiler/rustc_session/src/code_stats.rs
@@ -0,0 +1,182 @@
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::sync::Lock;
+use rustc_span::Symbol;
+use rustc_target::abi::{Align, Size};
+use std::cmp::{self, Ordering};
+
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+pub struct VariantInfo {
+ pub name: Option<Symbol>,
+ pub kind: SizeKind,
+ pub size: u64,
+ pub align: u64,
+ pub fields: Vec<FieldInfo>,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum SizeKind {
+ Exact,
+ Min,
+}
+
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+pub struct FieldInfo {
+ pub name: Symbol,
+ pub offset: u64,
+ pub size: u64,
+ pub align: u64,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum DataTypeKind {
+ Struct,
+ Union,
+ Enum,
+ Closure,
+}
+
+#[derive(PartialEq, Eq, Hash, Debug)]
+pub struct TypeSizeInfo {
+ pub kind: DataTypeKind,
+ pub type_description: String,
+ pub align: u64,
+ pub overall_size: u64,
+ pub packed: bool,
+ pub opt_discr_size: Option<u64>,
+ pub variants: Vec<VariantInfo>,
+}
+
+#[derive(Default)]
+pub struct CodeStats {
+ type_sizes: Lock<FxHashSet<TypeSizeInfo>>,
+}
+
+impl CodeStats {
+ pub fn record_type_size<S: ToString>(
+ &self,
+ kind: DataTypeKind,
+ type_desc: S,
+ align: Align,
+ overall_size: Size,
+ packed: bool,
+ opt_discr_size: Option<Size>,
+ mut variants: Vec<VariantInfo>,
+ ) {
+ // Sort variants so the largest ones are shown first. A stable sort is
+ // used here so that source code order is preserved for all variants
+ // that have the same size.
+ variants.sort_by(|info1, info2| info2.size.cmp(&info1.size));
+ let info = TypeSizeInfo {
+ kind,
+ type_description: type_desc.to_string(),
+ align: align.bytes(),
+ overall_size: overall_size.bytes(),
+ packed,
+ opt_discr_size: opt_discr_size.map(|s| s.bytes()),
+ variants,
+ };
+ self.type_sizes.borrow_mut().insert(info);
+ }
+
+ pub fn print_type_sizes(&self) {
+ let type_sizes = self.type_sizes.borrow();
+ let mut sorted: Vec<_> = type_sizes.iter().collect();
+
+ // Primary sort: large-to-small.
+ // Secondary sort: description (dictionary order)
+ sorted.sort_by(|info1, info2| {
+ // (reversing cmp order to get large-to-small ordering)
+ match info2.overall_size.cmp(&info1.overall_size) {
+ Ordering::Equal => info1.type_description.cmp(&info2.type_description),
+ other => other,
+ }
+ });
+
+ for info in sorted {
+ let TypeSizeInfo { type_description, overall_size, align, kind, variants, .. } = info;
+ println!(
+ "print-type-size type: `{type_description}`: {overall_size} bytes, alignment: {align} bytes"
+ );
+ let indent = " ";
+
+ let discr_size = if let Some(discr_size) = info.opt_discr_size {
+ println!("print-type-size {indent}discriminant: {discr_size} bytes");
+ discr_size
+ } else {
+ 0
+ };
+
+ // We start this at discr_size (rather than 0) because
+ // things like C-enums do not have variants but we still
+ // want the max_variant_size at the end of the loop below
+ // to reflect the presence of the discriminant.
+ let mut max_variant_size = discr_size;
+
+ let struct_like = match kind {
+ DataTypeKind::Struct | DataTypeKind::Closure => true,
+ DataTypeKind::Enum | DataTypeKind::Union => false,
+ };
+ for (i, variant_info) in variants.into_iter().enumerate() {
+ let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
+ let indent = if !struct_like {
+ let name = match name.as_ref() {
+ Some(name) => name.to_string(),
+ None => i.to_string(),
+ };
+ println!(
+ "print-type-size {indent}variant `{name}`: {diff} bytes",
+ diff = size - discr_size
+ );
+ " "
+ } else {
+ assert!(i < 1);
+ " "
+ };
+ max_variant_size = cmp::max(max_variant_size, size);
+
+ let mut min_offset = discr_size;
+
+ // We want to print fields by increasing offset. We also want
+ // zero-sized fields before non-zero-sized fields, otherwise
+ // the loop below goes wrong; hence the `f.size` in the sort
+ // key.
+ let mut fields = fields.clone();
+ fields.sort_by_key(|f| (f.offset, f.size));
+
+ for field in fields {
+ let FieldInfo { ref name, offset, size, align } = field;
+
+ if offset > min_offset {
+ let pad = offset - min_offset;
+ println!("print-type-size {indent}padding: {pad} bytes");
+ }
+
+ if offset < min_offset {
+ // If this happens it's probably a union.
+ println!(
+ "print-type-size {indent}field `.{name}`: {size} bytes, \
+ offset: {offset} bytes, \
+ alignment: {align} bytes"
+ );
+ } else if info.packed || offset == min_offset {
+ println!("print-type-size {indent}field `.{name}`: {size} bytes");
+ } else {
+ // Include field alignment in output only if it caused padding injection
+ println!(
+ "print-type-size {indent}field `.{name}`: {size} bytes, \
+ alignment: {align} bytes"
+ );
+ }
+
+ min_offset = offset + size;
+ }
+ }
+
+ match overall_size.checked_sub(max_variant_size) {
+ None => panic!("max_variant_size {max_variant_size} > {overall_size} overall_size"),
+ Some(diff @ 1..) => println!("print-type-size {indent}end padding: {diff} bytes"),
+ Some(0) => {}
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
new file mode 100644
index 000000000..6a8298605
--- /dev/null
+++ b/compiler/rustc_session/src/config.rs
@@ -0,0 +1,2970 @@
+//! Contains infrastructure for configuring the compiler, including parsing
+//! command-line options.
+
+pub use crate::options::*;
+
+use crate::search_paths::SearchPath;
+use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
+use crate::{early_error, early_warn, Session};
+use crate::{lint, HashStableContext};
+
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+
+use rustc_data_structures::stable_hasher::ToStableHashKey;
+use rustc_target::abi::{Align, TargetDataLayout};
+use rustc_target::spec::{LinkerFlavor, SplitDebuginfo, Target, TargetTriple, TargetWarnings};
+use rustc_target::spec::{PanicStrategy, SanitizerSet, TARGETS};
+
+use crate::parse::{CrateCheckConfig, CrateConfig};
+use rustc_feature::UnstableFeatures;
+use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION};
+use rustc_span::source_map::{FileName, FilePathMapping};
+use rustc_span::symbol::{sym, Symbol};
+use rustc_span::RealFileName;
+use rustc_span::SourceFileHashAlgorithm;
+
+use rustc_errors::emitter::HumanReadableErrorType;
+use rustc_errors::{ColorConfig, HandlerFlags};
+
+use std::collections::btree_map::{
+ Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter,
+};
+use std::collections::{BTreeMap, BTreeSet};
+use std::fmt;
+use std::hash::Hash;
+use std::iter::{self, FromIterator};
+use std::path::{Path, PathBuf};
+use std::str::{self, FromStr};
+
+/// The different settings that the `-C strip` flag can have.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum Strip {
+ /// Do not strip at all.
+ None,
+
+ /// Strip debuginfo.
+ Debuginfo,
+
+ /// Strip all symbols.
+ Symbols,
+}
+
+/// The different settings that the `-C control-flow-guard` flag can have.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum CFGuard {
+ /// Do not emit Control Flow Guard metadata or checks.
+ Disabled,
+
+ /// Emit Control Flow Guard metadata but no checks.
+ NoChecks,
+
+ /// Emit Control Flow Guard metadata and checks.
+ Checks,
+}
+
+/// The different settings that the `-Z cf-protection` flag can have.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum CFProtection {
+ /// Do not enable control-flow protection
+ None,
+
+ /// Emit control-flow protection for branches (enables indirect branch tracking).
+ Branch,
+
+ /// Emit control-flow protection for returns.
+ Return,
+
+ /// Emit control-flow protection for both branches and returns.
+ Full,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Hash, HashStable_Generic)]
+pub enum OptLevel {
+ No, // -O0
+ Less, // -O1
+ Default, // -O2
+ Aggressive, // -O3
+ Size, // -Os
+ SizeMin, // -Oz
+}
+
+/// This is what the `LtoCli` values get mapped to after resolving defaults and
+/// and taking other command line options into account.
+///
+/// Note that linker plugin-based LTO is a different mechanism entirely.
+#[derive(Clone, PartialEq)]
+pub enum Lto {
+ /// Don't do any LTO whatsoever.
+ No,
+
+ /// Do a full-crate-graph (inter-crate) LTO with ThinLTO.
+ Thin,
+
+ /// Do a local ThinLTO (intra-crate, over the CodeGen Units of the local crate only). This is
+ /// only relevant if multiple CGUs are used.
+ ThinLocal,
+
+ /// Do a full-crate-graph (inter-crate) LTO with "fat" LTO.
+ Fat,
+}
+
+/// The different settings that the `-C lto` flag can have.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum LtoCli {
+ /// `-C lto=no`
+ No,
+ /// `-C lto=yes`
+ Yes,
+ /// `-C lto`
+ NoParam,
+ /// `-C lto=thin`
+ Thin,
+ /// `-C lto=fat`
+ Fat,
+ /// No `-C lto` flag passed
+ Unspecified,
+}
+
+/// The different settings that the `-Z dump_mir_spanview` flag can have. `Statement` generates a
+/// document highlighting each span of every statement (including terminators). `Terminator` and
+/// `Block` highlight a single span per `BasicBlock`: the span of the block's `Terminator`, or a
+/// computed span for the block, representing the entire range, covering the block's terminator and
+/// all of its statements.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum MirSpanview {
+ /// Default `-Z dump_mir_spanview` or `-Z dump_mir_spanview=statement`
+ Statement,
+ /// `-Z dump_mir_spanview=terminator`
+ Terminator,
+ /// `-Z dump_mir_spanview=block`
+ Block,
+}
+
+/// The different settings that the `-C instrument-coverage` flag can have.
+///
+/// Coverage instrumentation now supports combining `-C instrument-coverage`
+/// with compiler and linker optimization (enabled with `-O` or `-C opt-level=1`
+/// and higher). Nevertheless, there are many variables, depending on options
+/// selected, code structure, and enabled attributes. If errors are encountered,
+/// either while compiling or when generating `llvm-cov show` reports, consider
+/// lowering the optimization level, including or excluding `-C link-dead-code`,
+/// or using `-Zunstable-options -C instrument-coverage=except-unused-functions`
+/// or `-Zunstable-options -C instrument-coverage=except-unused-generics`.
+///
+/// Note that `ExceptUnusedFunctions` means: When `mapgen.rs` generates the
+/// coverage map, it will not attempt to generate synthetic functions for unused
+/// (and not code-generated) functions (whether they are generic or not). As a
+/// result, non-codegenned functions will not be included in the coverage map,
+/// and will not appear, as covered or uncovered, in coverage reports.
+///
+/// `ExceptUnusedGenerics` will add synthetic functions to the coverage map,
+/// unless the function has type parameters.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum InstrumentCoverage {
+ /// Default `-C instrument-coverage` or `-C instrument-coverage=statement`
+ All,
+ /// `-Zunstable-options -C instrument-coverage=except-unused-generics`
+ ExceptUnusedGenerics,
+ /// `-Zunstable-options -C instrument-coverage=except-unused-functions`
+ ExceptUnusedFunctions,
+ /// `-C instrument-coverage=off` (or `no`, etc.)
+ Off,
+}
+
+#[derive(Clone, PartialEq, Hash, Debug)]
+pub enum LinkerPluginLto {
+ LinkerPlugin(PathBuf),
+ LinkerPluginAuto,
+ Disabled,
+}
+
+/// 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,
+}
+
+impl LinkerPluginLto {
+ pub fn enabled(&self) -> bool {
+ match *self {
+ LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true,
+ LinkerPluginLto::Disabled => false,
+ }
+ }
+}
+
+/// The different settings that can be enabled via the `-Z location-detail` flag.
+#[derive(Clone, PartialEq, Hash, Debug)]
+pub struct LocationDetail {
+ pub file: bool,
+ pub line: bool,
+ pub column: bool,
+}
+
+impl LocationDetail {
+ pub fn all() -> Self {
+ Self { file: true, line: true, column: true }
+ }
+}
+
+#[derive(Clone, PartialEq, Hash, Debug)]
+pub enum SwitchWithOptPath {
+ Enabled(Option<PathBuf>),
+ Disabled,
+}
+
+impl SwitchWithOptPath {
+ pub fn enabled(&self) -> bool {
+ match *self {
+ SwitchWithOptPath::Enabled(_) => true,
+ SwitchWithOptPath::Disabled => false,
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable_Generic)]
+#[derive(Encodable, Decodable)]
+pub enum SymbolManglingVersion {
+ Legacy,
+ V0,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Hash)]
+pub enum DebugInfo {
+ None,
+ Limited,
+ Full,
+}
+
+/// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split
+/// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform
+/// uses DWARF for debug-information.
+///
+/// Some debug-information requires link-time relocation and some does not. LLVM can partition
+/// the debuginfo into sections depending on whether or not it requires link-time relocation. Split
+/// DWARF provides a mechanism which allows the linker to skip the sections which don't require
+/// link-time relocation - either by putting those sections in DWARF object files, or by keeping
+/// them in the object file in such a way that the linker will skip them.
+#[derive(Clone, Copy, Debug, PartialEq, Hash)]
+pub enum SplitDwarfKind {
+ /// Sections which do not require relocation are written into object file but ignored by the
+ /// linker.
+ Single,
+ /// Sections which do not require relocation are written into a DWARF object (`.dwo`) file
+ /// which is ignored by the linker.
+ Split,
+}
+
+impl FromStr for SplitDwarfKind {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<Self, ()> {
+ Ok(match s {
+ "single" => SplitDwarfKind::Single,
+ "split" => SplitDwarfKind::Split,
+ _ => return Err(()),
+ })
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)]
+#[derive(Encodable, Decodable)]
+pub enum OutputType {
+ Bitcode,
+ Assembly,
+ LlvmAssembly,
+ Mir,
+ Metadata,
+ Object,
+ Exe,
+ DepInfo,
+}
+
+impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {
+ type KeyType = Self;
+
+ fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
+ *self
+ }
+}
+
+impl OutputType {
+ fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
+ match *self {
+ OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true,
+ OutputType::Bitcode
+ | OutputType::Assembly
+ | OutputType::LlvmAssembly
+ | OutputType::Mir
+ | OutputType::Object => false,
+ }
+ }
+
+ fn shorthand(&self) -> &'static str {
+ match *self {
+ OutputType::Bitcode => "llvm-bc",
+ OutputType::Assembly => "asm",
+ OutputType::LlvmAssembly => "llvm-ir",
+ OutputType::Mir => "mir",
+ OutputType::Object => "obj",
+ OutputType::Metadata => "metadata",
+ OutputType::Exe => "link",
+ OutputType::DepInfo => "dep-info",
+ }
+ }
+
+ fn from_shorthand(shorthand: &str) -> Option<Self> {
+ Some(match shorthand {
+ "asm" => OutputType::Assembly,
+ "llvm-ir" => OutputType::LlvmAssembly,
+ "mir" => OutputType::Mir,
+ "llvm-bc" => OutputType::Bitcode,
+ "obj" => OutputType::Object,
+ "metadata" => OutputType::Metadata,
+ "link" => OutputType::Exe,
+ "dep-info" => OutputType::DepInfo,
+ _ => return None,
+ })
+ }
+
+ fn shorthands_display() -> String {
+ format!(
+ "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`",
+ OutputType::Bitcode.shorthand(),
+ OutputType::Assembly.shorthand(),
+ OutputType::LlvmAssembly.shorthand(),
+ OutputType::Mir.shorthand(),
+ OutputType::Object.shorthand(),
+ OutputType::Metadata.shorthand(),
+ OutputType::Exe.shorthand(),
+ OutputType::DepInfo.shorthand(),
+ )
+ }
+
+ pub fn extension(&self) -> &'static str {
+ match *self {
+ OutputType::Bitcode => "bc",
+ OutputType::Assembly => "s",
+ OutputType::LlvmAssembly => "ll",
+ OutputType::Mir => "mir",
+ OutputType::Object => "o",
+ OutputType::Metadata => "rmeta",
+ OutputType::DepInfo => "d",
+ OutputType::Exe => "",
+ }
+ }
+}
+
+/// The type of diagnostics output to generate.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ErrorOutputType {
+ /// Output meant for the consumption of humans.
+ HumanReadable(HumanReadableErrorType),
+ /// Output that's consumed by other tools such as `rustfix` or the `RLS`.
+ Json {
+ /// Render the JSON in a human readable way (with indents and newlines).
+ pretty: bool,
+ /// The JSON output includes a `rendered` field that includes the rendered
+ /// human output.
+ json_rendered: HumanReadableErrorType,
+ },
+}
+
+impl Default for ErrorOutputType {
+ fn default() -> Self {
+ Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto))
+ }
+}
+
+/// Parameter to control path trimming.
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
+pub enum TrimmedDefPaths {
+ /// `try_print_trimmed_def_path` never prints a trimmed path and never calls the expensive query
+ #[default]
+ Never,
+ /// `try_print_trimmed_def_path` calls the expensive query, the query doesn't call `delay_good_path_bug`
+ Always,
+ /// `try_print_trimmed_def_path` calls the expensive query, the query calls `delay_good_path_bug`
+ GoodPath,
+}
+
+/// 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)]
+pub struct OutputTypes(BTreeMap<OutputType, Option<PathBuf>>);
+
+impl OutputTypes {
+ pub fn new(entries: &[(OutputType, Option<PathBuf>)]) -> OutputTypes {
+ OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone()))))
+ }
+
+ pub fn get(&self, key: &OutputType) -> Option<&Option<PathBuf>> {
+ self.0.get(key)
+ }
+
+ pub fn contains_key(&self, key: &OutputType) -> bool {
+ self.0.contains_key(key)
+ }
+
+ pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<PathBuf>> {
+ self.0.keys()
+ }
+
+ pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<PathBuf>> {
+ self.0.values()
+ }
+
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ /// Returns `true` if any of the output types require codegen or linking.
+ pub fn should_codegen(&self) -> bool {
+ self.0.keys().any(|k| match *k {
+ OutputType::Bitcode
+ | OutputType::Assembly
+ | OutputType::LlvmAssembly
+ | OutputType::Mir
+ | OutputType::Object
+ | OutputType::Exe => true,
+ OutputType::Metadata | OutputType::DepInfo => false,
+ })
+ }
+
+ /// 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::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,
+}
+
+#[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()
+ }
+
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+}
+
+impl ExternEntry {
+ fn new(location: ExternLocation) -> ExternEntry {
+ ExternEntry { location, is_private_dep: false, add_prelude: false, nounused_dep: false }
+ }
+
+ pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> {
+ match &self.location {
+ ExternLocation::ExactPaths(set) => Some(set.iter()),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum PrintRequest {
+ FileNames,
+ Sysroot,
+ TargetLibdir,
+ CrateName,
+ Cfg,
+ TargetList,
+ TargetCPUs,
+ TargetFeatures,
+ RelocationModels,
+ CodeModels,
+ TlsModels,
+ TargetSpec,
+ NativeStaticLibs,
+ StackProtectorStrategies,
+ LinkArgs,
+}
+
+pub enum Input {
+ /// Load source code from a file.
+ File(PathBuf),
+ /// Load source code from a string.
+ Str {
+ /// A string that is shown in place of a filename.
+ name: FileName,
+ /// An anonymous string containing the source code.
+ input: String,
+ },
+}
+
+impl Input {
+ pub fn filestem(&self) -> &str {
+ match *self {
+ Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(),
+ Input::Str { .. } => "rust_out",
+ }
+ }
+
+ pub fn source_name(&self) -> FileName {
+ match *self {
+ Input::File(ref ifile) => ifile.clone().into(),
+ Input::Str { ref name, .. } => name.clone(),
+ }
+ }
+}
+
+#[derive(Clone, Hash, Debug, HashStable_Generic)]
+pub struct OutputFilenames {
+ pub out_directory: PathBuf,
+ filestem: String,
+ pub single_output_file: Option<PathBuf>,
+ pub temps_directory: Option<PathBuf>,
+ pub outputs: OutputTypes,
+}
+
+pub const RLINK_EXT: &str = "rlink";
+pub const RUST_CGU_EXT: &str = "rcgu";
+pub const DWARF_OBJECT_EXT: &str = "dwo";
+
+impl OutputFilenames {
+ pub fn new(
+ out_directory: PathBuf,
+ out_filestem: String,
+ single_output_file: Option<PathBuf>,
+ temps_directory: Option<PathBuf>,
+ extra: String,
+ outputs: OutputTypes,
+ ) -> Self {
+ OutputFilenames {
+ out_directory,
+ single_output_file,
+ temps_directory,
+ outputs,
+ filestem: format!("{out_filestem}{extra}"),
+ }
+ }
+
+ pub fn path(&self, flavor: OutputType) -> PathBuf {
+ self.outputs
+ .get(&flavor)
+ .and_then(|p| p.to_owned())
+ .or_else(|| self.single_output_file.clone())
+ .unwrap_or_else(|| self.output_path(flavor))
+ }
+
+ /// Gets the output path where a compilation artifact of the given type
+ /// should be placed on disk.
+ pub fn output_path(&self, flavor: OutputType) -> PathBuf {
+ let extension = flavor.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(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf {
+ let extension = flavor.extension();
+ self.temp_path_ext(extension, codegen_unit_name)
+ }
+
+ /// Like `temp_path`, but specifically for dwarf objects.
+ pub fn temp_path_dwo(&self, codegen_unit_name: Option<&str>) -> PathBuf {
+ self.temp_path_ext(DWARF_OBJECT_EXT, codegen_unit_name)
+ }
+
+ /// Like `temp_path`, but also supports things where there is no corresponding
+ /// OutputType, like noopt-bitcode or lto-bitcode.
+ pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf {
+ let mut extension = String::new();
+
+ if let Some(codegen_unit_name) = codegen_unit_name {
+ extension.push_str(codegen_unit_name);
+ }
+
+ if !ext.is_empty() {
+ if !extension.is_empty() {
+ extension.push('.');
+ extension.push_str(RUST_CGU_EXT);
+ extension.push('.');
+ }
+
+ extension.push_str(ext);
+ }
+
+ let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
+
+ self.with_directory_and_extension(&temps_directory, &extension)
+ }
+
+ pub fn with_extension(&self, extension: &str) -> PathBuf {
+ self.with_directory_and_extension(&self.out_directory, extension)
+ }
+
+ fn with_directory_and_extension(&self, directory: &PathBuf, 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: Option<&str>,
+ ) -> Option<PathBuf> {
+ let obj_out = self.temp_path(OutputType::Object, cgu_name);
+ let dwo_out = self.temp_path_dwo(cgu_name);
+ 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 fn host_triple() -> &'static str {
+ // Get the host triple out of the build environment. This ensures that our
+ // idea of the host triple is the same as for the set of libraries we've
+ // actually built. We can't just take LLVM's host triple because they
+ // normalize all ix86 architectures to i386.
+ //
+ // Instead of grabbing the host triple (for the current host), we grab (at
+ // compile time) the target triple that this rustc is built with and
+ // calling that (at runtime) the host triple.
+ (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE")
+}
+
+impl Default for Options {
+ fn default() -> Options {
+ Options {
+ 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![],
+ maybe_sysroot: None,
+ target_triple: TargetTriple::from_triple(host_triple()),
+ test: false,
+ incremental: None,
+ unstable_opts: Default::default(),
+ prints: Vec::new(),
+ cg: Default::default(),
+ error_format: ErrorOutputType::default(),
+ diagnostic_width: None,
+ externs: Externs(BTreeMap::new()),
+ crate_name: None,
+ libs: Vec::new(),
+ unstable_features: UnstableFeatures::Disallow,
+ debug_assertions: true,
+ actually_rustdoc: false,
+ trimmed_def_paths: TrimmedDefPaths::default(),
+ cli_forced_codegen_units: None,
+ cli_forced_thinlto_off: false,
+ remap_path_prefix: Vec::new(),
+ real_rust_source_base_dir: None,
+ edition: DEFAULT_EDITION,
+ json_artifact_notifications: false,
+ json_unused_externs: JsonUnusedExterns::No,
+ json_future_incompat: false,
+ pretty: None,
+ working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
+ }
+ }
+}
+
+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 {
+ FilePathMapping::new(self.remap_path_prefix.clone())
+ }
+
+ /// Returns `true` if there will be an output file generated.
+ pub fn will_create_output_file(&self) -> bool {
+ !self.unstable_opts.parse_only && // The file is just being parsed
+ !self.unstable_opts.ls // 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::Default | OptLevel::Aggressive => false,
+ },
+ }
+ }
+
+ pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion {
+ self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy)
+ }
+}
+
+impl UnstableOptions {
+ pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags {
+ HandlerFlags {
+ can_emit_warnings,
+ treat_err_as_bug: self.treat_err_as_bug,
+ dont_buffer_diagnostics: self.dont_buffer_diagnostics,
+ report_delayed_bugs: self.report_delayed_bugs,
+ macro_backtrace: self.macro_backtrace,
+ deduplicate_diagnostics: self.deduplicate_diagnostics,
+ }
+ }
+}
+
+// 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,
+ Start,
+}
+
+#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
+#[derive(HashStable_Generic)]
+pub enum CrateType {
+ Executable,
+ Dylib,
+ Rlib,
+ Staticlib,
+ Cdylib,
+ ProcMacro,
+}
+
+impl CrateType {
+ /// When generated, is this crate type an archive?
+ pub fn is_archive(&self) -> bool {
+ match *self {
+ CrateType::Rlib | CrateType::Staticlib => true,
+ CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => {
+ false
+ }
+ }
+ }
+}
+
+#[derive(Clone, Hash, Debug, PartialEq, Eq)]
+pub enum Passes {
+ Some(Vec<String>),
+ All,
+}
+
+impl Passes {
+ pub fn is_empty(&self) -> bool {
+ match *self {
+ Passes::Some(ref v) => v.is_empty(),
+ Passes::All => false,
+ }
+ }
+
+ pub 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 key: PAuthKey,
+}
+
+#[derive(Clone, Copy, Hash, Debug, PartialEq)]
+pub struct BranchProtection {
+ pub bti: bool,
+ pub pac_ret: Option<PacRet>,
+}
+
+impl Default for BranchProtection {
+ fn default() -> Self {
+ BranchProtection { bti: false, pac_ret: None }
+ }
+}
+
+pub const fn default_lib_output() -> CrateType {
+ CrateType::Rlib
+}
+
+fn default_configuration(sess: &Session) -> CrateConfig {
+ // NOTE: This should be kept in sync with `CrateCheckConfig::fill_well_known` below.
+ let end = &sess.target.endian;
+ let arch = &sess.target.arch;
+ let wordsz = sess.target.pointer_width.to_string();
+ let os = &sess.target.os;
+ let env = &sess.target.env;
+ let abi = &sess.target.abi;
+ let vendor = &sess.target.vendor;
+ let min_atomic_width = sess.target.min_atomic_width();
+ let max_atomic_width = sess.target.max_atomic_width();
+ let atomic_cas = sess.target.atomic_cas;
+ let layout = TargetDataLayout::parse(&sess.target).unwrap_or_else(|err| {
+ sess.fatal(&err);
+ });
+
+ let mut ret = FxHashSet::default();
+ ret.reserve(7); // the minimum number of insertions
+ // Target bindings.
+ ret.insert((sym::target_os, Some(Symbol::intern(os))));
+ for fam in sess.target.families.as_ref() {
+ ret.insert((sym::target_family, Some(Symbol::intern(fam))));
+ if fam == "windows" {
+ ret.insert((sym::windows, None));
+ } else if fam == "unix" {
+ ret.insert((sym::unix, None));
+ }
+ }
+ ret.insert((sym::target_arch, Some(Symbol::intern(arch))));
+ ret.insert((sym::target_endian, Some(Symbol::intern(end.as_str()))));
+ ret.insert((sym::target_pointer_width, Some(Symbol::intern(&wordsz))));
+ ret.insert((sym::target_env, Some(Symbol::intern(env))));
+ ret.insert((sym::target_abi, Some(Symbol::intern(abi))));
+ ret.insert((sym::target_vendor, Some(Symbol::intern(vendor))));
+ if sess.target.has_thread_local {
+ ret.insert((sym::target_thread_local, None));
+ }
+ for (i, align) in [
+ (8, layout.i8_align.abi),
+ (16, layout.i16_align.abi),
+ (32, layout.i32_align.abi),
+ (64, layout.i64_align.abi),
+ (128, layout.i128_align.abi),
+ ] {
+ if i >= min_atomic_width && i <= max_atomic_width {
+ let mut insert_atomic = |s, align: Align| {
+ ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s))));
+ if atomic_cas {
+ ret.insert((sym::target_has_atomic, Some(Symbol::intern(s))));
+ }
+ if align.bits() == i {
+ ret.insert((sym::target_has_atomic_equal_alignment, Some(Symbol::intern(s))));
+ }
+ };
+ let s = i.to_string();
+ insert_atomic(&s, align);
+ if s == wordsz {
+ insert_atomic("ptr", layout.pointer_align.abi);
+ }
+ }
+ }
+
+ let panic_strategy = sess.panic_strategy();
+ ret.insert((sym::panic, Some(panic_strategy.desc_symbol())));
+
+ for s in sess.opts.unstable_opts.sanitizer {
+ let symbol = Symbol::intern(&s.to_string());
+ ret.insert((sym::sanitize, Some(symbol)));
+ }
+
+ if sess.opts.debug_assertions {
+ ret.insert((sym::debug_assertions, None));
+ }
+ // JUSTIFICATION: before wrapper fn is available
+ #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+ if sess.opts.crate_types.contains(&CrateType::ProcMacro) {
+ ret.insert((sym::proc_macro, None));
+ }
+ ret
+}
+
+/// Converts the crate `cfg!` configuration from `String` to `Symbol`.
+/// `rustc_interface::interface::Config` accepts this in the compiler configuration,
+/// but the symbol interner is not yet set up then, so we must convert it later.
+pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig {
+ cfg.into_iter().map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))).collect()
+}
+
+/// The parsed `--check-cfg` options
+pub struct CheckCfg<T = String> {
+ /// The set of all `names()`, if None no name checking is performed
+ pub names_valid: Option<FxHashSet<T>>,
+ /// Is well known values activated
+ pub well_known_values: bool,
+ /// The set of all `values()`
+ pub values_valid: FxHashMap<T, FxHashSet<T>>,
+}
+
+impl<T> Default for CheckCfg<T> {
+ fn default() -> Self {
+ CheckCfg {
+ names_valid: Default::default(),
+ values_valid: Default::default(),
+ well_known_values: false,
+ }
+ }
+}
+
+impl<T> CheckCfg<T> {
+ fn map_data<O: Eq + Hash>(&self, f: impl Fn(&T) -> O) -> CheckCfg<O> {
+ CheckCfg {
+ names_valid: self
+ .names_valid
+ .as_ref()
+ .map(|names_valid| names_valid.iter().map(|a| f(a)).collect()),
+ values_valid: self
+ .values_valid
+ .iter()
+ .map(|(a, b)| (f(a), b.iter().map(|b| f(b)).collect()))
+ .collect(),
+ well_known_values: self.well_known_values,
+ }
+ }
+}
+
+/// Converts the crate `--check-cfg` options from `String` to `Symbol`.
+/// `rustc_interface::interface::Config` accepts this in the compiler configuration,
+/// but the symbol interner is not yet set up then, so we must convert it later.
+pub fn to_crate_check_config(cfg: CheckCfg) -> CrateCheckConfig {
+ cfg.map_data(|s| Symbol::intern(s))
+}
+
+impl CrateCheckConfig {
+ /// Fills a `CrateCheckConfig` with well-known configuration names.
+ fn fill_well_known_names(&mut self) {
+ // NOTE: This should be kept in sync with `default_configuration` and
+ // `fill_well_known_values`
+ const WELL_KNOWN_NAMES: &[Symbol] = &[
+ // rustc
+ sym::unix,
+ sym::windows,
+ sym::target_os,
+ sym::target_family,
+ sym::target_arch,
+ sym::target_endian,
+ sym::target_pointer_width,
+ sym::target_env,
+ sym::target_abi,
+ sym::target_vendor,
+ sym::target_thread_local,
+ sym::target_has_atomic_load_store,
+ sym::target_has_atomic,
+ sym::target_has_atomic_equal_alignment,
+ sym::target_feature,
+ sym::panic,
+ sym::sanitize,
+ sym::debug_assertions,
+ sym::proc_macro,
+ sym::test,
+ sym::feature,
+ // rustdoc
+ sym::doc,
+ sym::doctest,
+ // miri
+ sym::miri,
+ ];
+
+ // We only insert well-known names if `names()` was activated
+ if let Some(names_valid) = &mut self.names_valid {
+ names_valid.extend(WELL_KNOWN_NAMES);
+ }
+ }
+
+ /// Fills a `CrateCheckConfig` with well-known configuration values.
+ fn fill_well_known_values(&mut self) {
+ if !self.well_known_values {
+ return;
+ }
+
+ // NOTE: This should be kept in sync with `default_configuration` and
+ // `fill_well_known_names`
+
+ let panic_values = &PanicStrategy::all();
+
+ let atomic_values = &[
+ sym::ptr,
+ sym::integer(8usize),
+ sym::integer(16usize),
+ sym::integer(32usize),
+ sym::integer(64usize),
+ sym::integer(128usize),
+ ];
+
+ let sanitize_values = SanitizerSet::all()
+ .into_iter()
+ .map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap()));
+
+ // Unknown possible values:
+ // - `feature`
+ // - `target_feature`
+
+ // No-values
+ for name in [
+ sym::doc,
+ sym::miri,
+ sym::unix,
+ sym::test,
+ sym::doctest,
+ sym::windows,
+ sym::proc_macro,
+ sym::debug_assertions,
+ sym::target_thread_local,
+ ] {
+ self.values_valid.entry(name).or_default();
+ }
+
+ // Pre-defined values
+ self.values_valid.entry(sym::panic).or_default().extend(panic_values);
+ self.values_valid.entry(sym::sanitize).or_default().extend(sanitize_values);
+ self.values_valid.entry(sym::target_has_atomic).or_default().extend(atomic_values);
+ self.values_valid
+ .entry(sym::target_has_atomic_load_store)
+ .or_default()
+ .extend(atomic_values);
+ self.values_valid
+ .entry(sym::target_has_atomic_equal_alignment)
+ .or_default()
+ .extend(atomic_values);
+
+ // Target specific values
+ {
+ const VALUES: [&Symbol; 8] = [
+ &sym::target_os,
+ &sym::target_family,
+ &sym::target_arch,
+ &sym::target_endian,
+ &sym::target_env,
+ &sym::target_abi,
+ &sym::target_vendor,
+ &sym::target_pointer_width,
+ ];
+
+ // Initialize (if not already initialized)
+ for &e in VALUES {
+ self.values_valid.entry(e).or_default();
+ }
+
+ // Get all values map at once otherwise it would be costly.
+ // (8 values * 220 targets ~= 1760 times, at the time of writing this comment).
+ let [
+ values_target_os,
+ values_target_family,
+ values_target_arch,
+ values_target_endian,
+ values_target_env,
+ values_target_abi,
+ values_target_vendor,
+ values_target_pointer_width,
+ ] = self
+ .values_valid
+ .get_many_mut(VALUES)
+ .expect("unable to get all the check-cfg values buckets");
+
+ for target in TARGETS
+ .iter()
+ .map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
+ {
+ values_target_os.insert(Symbol::intern(&target.options.os));
+ values_target_family
+ .extend(target.options.families.iter().map(|family| Symbol::intern(family)));
+ values_target_arch.insert(Symbol::intern(&target.arch));
+ values_target_endian.insert(Symbol::intern(&target.options.endian.as_str()));
+ values_target_env.insert(Symbol::intern(&target.options.env));
+ values_target_abi.insert(Symbol::intern(&target.options.abi));
+ values_target_vendor.insert(Symbol::intern(&target.options.vendor));
+ values_target_pointer_width.insert(sym::integer(target.pointer_width));
+ }
+ }
+ }
+
+ pub fn fill_well_known(&mut self) {
+ self.fill_well_known_names();
+ self.fill_well_known_values();
+ }
+}
+
+pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig {
+ // Combine the configuration requested by the session (command line) with
+ // some default and generated configuration items.
+ let default_cfg = default_configuration(sess);
+ // If the user wants a test runner, then add the test cfg.
+ if sess.opts.test {
+ user_cfg.insert((sym::test, None));
+ }
+ user_cfg.extend(default_cfg.iter().cloned());
+ user_cfg
+}
+
+pub(super) fn build_target_config(
+ opts: &Options,
+ target_override: Option<Target>,
+ sysroot: &Path,
+) -> Target {
+ let target_result = target_override.map_or_else(
+ || Target::search(&opts.target_triple, sysroot),
+ |t| Ok((t, TargetWarnings::empty())),
+ );
+ let (target, target_warnings) = target_result.unwrap_or_else(|e| {
+ early_error(
+ opts.error_format,
+ &format!(
+ "Error loading target specification: {}. \
+ Run `rustc --print target-list` for a list of built-in targets",
+ e
+ ),
+ )
+ });
+ for warning in target_warnings.warning_messages() {
+ early_warn(opts.error_format, &warning)
+ }
+
+ if !matches!(target.pointer_width, 16 | 32 | 64) {
+ early_error(
+ opts.error_format,
+ &format!(
+ "target specification was invalid: \
+ unrecognized target-pointer-width {}",
+ target.pointer_width
+ ),
+ )
+ }
+
+ target
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum OptionStability {
+ Stable,
+ Unstable,
+}
+
+pub struct RustcOptGroup {
+ pub apply: Box<dyn Fn(&mut getopts::Options) -> &mut getopts::Options>,
+ pub name: &'static str,
+ pub stability: OptionStability,
+}
+
+impl RustcOptGroup {
+ pub fn is_stable(&self) -> bool {
+ self.stability == OptionStability::Stable
+ }
+
+ pub fn stable<F>(name: &'static str, f: F) -> RustcOptGroup
+ where
+ F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static,
+ {
+ RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Stable }
+ }
+
+ pub fn unstable<F>(name: &'static str, f: F) -> RustcOptGroup
+ where
+ F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static,
+ {
+ RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Unstable }
+ }
+}
+
+// The `opt` local module holds wrappers around the `getopts` API that
+// adds extra rustc-specific metadata to each option; such metadata
+// is exposed by . The public
+// functions below ending with `_u` are the functions that return
+// *unstable* options, i.e., options that are only enabled when the
+// user also passes the `-Z unstable-options` debugging flag.
+mod opt {
+ // The `fn flag*` etc below are written so that we can use them
+ // in the future; do not warn about them not being used right now.
+ #![allow(dead_code)]
+
+ use super::RustcOptGroup;
+
+ pub type R = RustcOptGroup;
+ pub type S = &'static str;
+
+ fn stable<F>(name: S, f: F) -> R
+ where
+ F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static,
+ {
+ RustcOptGroup::stable(name, f)
+ }
+
+ fn unstable<F>(name: S, f: F) -> R
+ where
+ F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static,
+ {
+ RustcOptGroup::unstable(name, f)
+ }
+
+ fn longer(a: S, b: S) -> S {
+ if a.len() > b.len() { a } else { b }
+ }
+
+ pub fn opt_s(a: S, b: S, c: S, d: S) -> R {
+ stable(longer(a, b), move |opts| opts.optopt(a, b, c, d))
+ }
+ pub fn multi_s(a: S, b: S, c: S, d: S) -> R {
+ stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d))
+ }
+ pub fn flag_s(a: S, b: S, c: S) -> R {
+ stable(longer(a, b), move |opts| opts.optflag(a, b, c))
+ }
+ pub fn flagmulti_s(a: S, b: S, c: S) -> R {
+ stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c))
+ }
+
+ pub fn opt(a: S, b: S, c: S, d: S) -> R {
+ unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d))
+ }
+ pub fn multi(a: S, b: S, c: S, d: S) -> R {
+ unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d))
+ }
+}
+
+/// Returns the "short" subset of the rustc command line options,
+/// including metadata for each option, such as whether the option is
+/// part of the stable long-term interface for rustc.
+pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
+ vec![
+ opt::flag_s("h", "help", "Display this message"),
+ opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"),
+ opt::multi("", "check-cfg", "Provide list of valid cfg options for checking", "SPEC"),
+ opt::multi_s(
+ "L",
+ "",
+ "Add a directory to the library search path. The
+ optional KIND can be one of dependency, crate, native,
+ framework, or all (the default).",
+ "[KIND=]PATH",
+ ),
+ opt::multi_s(
+ "l",
+ "",
+ "Link the generated crate(s) to the specified native
+ library NAME. The optional KIND can be one of
+ static, framework, or dylib (the default).
+ Optional comma separated MODIFIERS (bundle|verbatim|whole-archive|as-needed)
+ may be specified each with a prefix of either '+' to
+ enable or '-' to disable.",
+ "[KIND[:MODIFIERS]=]NAME[:RENAME]",
+ ),
+ make_crate_type_option(),
+ opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"),
+ opt::opt_s(
+ "",
+ "edition",
+ "Specify which edition of the compiler to use when compiling code.",
+ EDITION_NAME_LIST,
+ ),
+ opt::multi_s(
+ "",
+ "emit",
+ "Comma separated list of types of output for \
+ the compiler to emit",
+ "[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]",
+ ),
+ opt::multi_s(
+ "",
+ "print",
+ "Compiler information to print on stdout",
+ "[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\
+ target-cpus|target-features|relocation-models|code-models|\
+ tls-models|target-spec-json|native-static-libs|stack-protector-strategies|\
+ link-args]",
+ ),
+ opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"),
+ opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"),
+ opt::opt_s("o", "", "Write output to <filename>", "FILENAME"),
+ opt::opt_s(
+ "",
+ "out-dir",
+ "Write output to compiler-chosen filename \
+ in <dir>",
+ "DIR",
+ ),
+ opt::opt_s(
+ "",
+ "explain",
+ "Provide a detailed explanation of an error \
+ message",
+ "OPT",
+ ),
+ opt::flag_s("", "test", "Build a test harness"),
+ opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"),
+ opt::multi_s("A", "allow", "Set lint allowed", "LINT"),
+ opt::multi_s("W", "warn", "Set lint warnings", "LINT"),
+ opt::multi_s("", "force-warn", "Set lint force-warn", "LINT"),
+ opt::multi_s("D", "deny", "Set lint denied", "LINT"),
+ opt::multi_s("F", "forbid", "Set lint forbidden", "LINT"),
+ opt::multi_s(
+ "",
+ "cap-lints",
+ "Set the most restrictive lint level. \
+ More restrictive lints are capped at this \
+ level",
+ "LEVEL",
+ ),
+ opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
+ opt::flag_s("V", "version", "Print version info and exit"),
+ opt::flag_s("v", "verbose", "Use verbose output"),
+ ]
+}
+
+/// Returns all rustc command line options, including metadata for
+/// each option, such as whether the option is part of the stable
+/// long-term interface for rustc.
+pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
+ let mut opts = rustc_short_optgroups();
+ // FIXME: none of these descriptions are actually used
+ opts.extend(vec![
+ opt::multi_s(
+ "",
+ "extern",
+ "Specify where an external rust library is located",
+ "NAME[=PATH]",
+ ),
+ opt::opt_s("", "sysroot", "Override the system root", "PATH"),
+ opt::multi("Z", "", "Set unstable / perma-unstable options", "FLAG"),
+ opt::opt_s(
+ "",
+ "error-format",
+ "How errors and other messages are produced",
+ "human|json|short",
+ ),
+ opt::multi_s("", "json", "Configure the JSON output of the compiler", "CONFIG"),
+ opt::opt_s(
+ "",
+ "color",
+ "Configure coloring of output:
+ auto = colorize, if output goes to a tty (default);
+ always = always colorize output;
+ never = never colorize output",
+ "auto|always|never",
+ ),
+ opt::opt_s(
+ "",
+ "diagnostic-width",
+ "Inform rustc of the width of the output so that diagnostics can be truncated to fit",
+ "WIDTH",
+ ),
+ opt::multi_s(
+ "",
+ "remap-path-prefix",
+ "Remap source names in all output (compiler messages and output files)",
+ "FROM=TO",
+ ),
+ ]);
+ opts
+}
+
+pub fn get_cmd_lint_options(
+ matches: &getopts::Matches,
+ error_format: ErrorOutputType,
+) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) {
+ let mut lint_opts_with_position = vec![];
+ let mut describe_lints = false;
+
+ for level in [lint::Allow, lint::Warn, lint::ForceWarn(None), 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_error(error_format, &format!("unknown lint level: `{cap}`")))
+ });
+
+ (lint_opts, describe_lints, lint_cap)
+}
+
+/// Parses the `--color` flag.
+pub fn parse_color(matches: &getopts::Matches) -> ColorConfig {
+ match matches.opt_str("color").as_ref().map(|s| &s[..]) {
+ Some("auto") => ColorConfig::Auto,
+ Some("always") => ColorConfig::Always,
+ Some("never") => ColorConfig::Never,
+
+ None => ColorConfig::Auto,
+
+ Some(arg) => early_error(
+ ErrorOutputType::default(),
+ &format!(
+ "argument for `--color` must be auto, \
+ always or never (instead was `{arg}`)"
+ ),
+ ),
+ }
+}
+
+/// Possible json config files
+pub struct JsonConfig {
+ pub json_rendered: HumanReadableErrorType,
+ pub json_artifact_notifications: bool,
+ pub json_unused_externs: JsonUnusedExterns,
+ pub 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(matches: &getopts::Matches) -> JsonConfig {
+ let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType =
+ HumanReadableErrorType::Default;
+ let mut json_color = ColorConfig::Never;
+ let mut json_artifact_notifications = false;
+ let mut json_unused_externs = JsonUnusedExterns::No;
+ let mut json_future_incompat = false;
+ for option in matches.opt_strs("json") {
+ // For now conservatively forbid `--color` with `--json` since `--json`
+ // won't actually be emitting any colors and anything colorized is
+ // embedded in a diagnostic message anyway.
+ if matches.opt_str("color").is_some() {
+ early_error(
+ ErrorOutputType::default(),
+ "cannot specify the `--color` option with `--json`",
+ );
+ }
+
+ for sub_option in option.split(',') {
+ match sub_option {
+ "diagnostic-short" => json_rendered = HumanReadableErrorType::Short,
+ "diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
+ "artifacts" => json_artifact_notifications = true,
+ "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
+ "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
+ "future-incompat" => json_future_incompat = true,
+ s => early_error(
+ ErrorOutputType::default(),
+ &format!("unknown `--json` option `{s}`"),
+ ),
+ }
+ }
+ }
+
+ JsonConfig {
+ json_rendered: json_rendered(json_color),
+ json_artifact_notifications,
+ json_unused_externs,
+ json_future_incompat,
+ }
+}
+
+/// Parses the `--error-format` flag.
+pub fn parse_error_format(
+ matches: &getopts::Matches,
+ color: ColorConfig,
+ json_rendered: HumanReadableErrorType,
+) -> ErrorOutputType {
+ // We need the `opts_present` check because the driver will send us Matches
+ // with only stable options if no unstable options are used. Since error-format
+ // is unstable, it will not be present. We have to use `opts_present` not
+ // `opt_present` because the latter will panic.
+ let error_format = if matches.opts_present(&["error-format".to_owned()]) {
+ match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
+ None | Some("human") => {
+ ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color))
+ }
+ Some("human-annotate-rs") => {
+ ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color))
+ }
+ Some("json") => ErrorOutputType::Json { pretty: false, json_rendered },
+ Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered },
+ Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)),
+
+ Some(arg) => early_error(
+ ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)),
+ &format!(
+ "argument for `--error-format` must be `human`, `json` or \
+ `short` (instead was `{arg}`)"
+ ),
+ ),
+ }
+ } else {
+ ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color))
+ };
+
+ match error_format {
+ ErrorOutputType::Json { .. } => {}
+
+ // Conservatively require that the `--json` argument is coupled with
+ // `--error-format=json`. This means that `--json` is specified we
+ // should actually be emitting JSON blobs.
+ _ if !matches.opt_strs("json").is_empty() => {
+ early_error(
+ ErrorOutputType::default(),
+ "using `--json` requires also using `--error-format=json`",
+ );
+ }
+
+ _ => {}
+ }
+
+ error_format
+}
+
+pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition {
+ let edition = match matches.opt_str("edition") {
+ Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| {
+ early_error(
+ ErrorOutputType::default(),
+ &format!(
+ "argument for `--edition` must be one of: \
+ {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 {}, but the latest edition supported by this Rust version is {}",
+ edition, LATEST_STABLE_EDITION
+ )
+ } else {
+ format!("edition {edition} is unstable and only available with -Z unstable-options")
+ };
+ early_error(ErrorOutputType::default(), &msg)
+ }
+
+ edition
+}
+
+fn check_error_format_stability(
+ unstable_opts: &UnstableOptions,
+ error_format: ErrorOutputType,
+ json_rendered: HumanReadableErrorType,
+) {
+ if !unstable_opts.unstable_options {
+ if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format {
+ early_error(
+ ErrorOutputType::Json { pretty: false, json_rendered },
+ "`--error-format=pretty-json` is unstable",
+ );
+ }
+ if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) =
+ error_format
+ {
+ early_error(
+ ErrorOutputType::Json { pretty: false, json_rendered },
+ "`--error-format=human-annotate-rs` is unstable",
+ );
+ }
+ }
+}
+
+fn parse_output_types(
+ unstable_opts: &UnstableOptions,
+ matches: &getopts::Matches,
+ error_format: ErrorOutputType,
+) -> OutputTypes {
+ let mut output_types = BTreeMap::new();
+ if !unstable_opts.parse_only {
+ for list in matches.opt_strs("emit") {
+ for output_type in list.split(',') {
+ let (shorthand, path) = match output_type.split_once('=') {
+ None => (output_type, None),
+ Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))),
+ };
+ let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
+ early_error(
+ error_format,
+ &format!(
+ "unknown emission type: `{shorthand}` - expected one of: {display}",
+ display = OutputType::shorthands_display(),
+ ),
+ )
+ });
+ output_types.insert(output_type, path);
+ }
+ }
+ };
+ if output_types.is_empty() {
+ output_types.insert(OutputType::Exe, None);
+ }
+ OutputTypes(output_types)
+}
+
+fn should_override_cgus_and_disable_thinlto(
+ output_types: &OutputTypes,
+ matches: &getopts::Matches,
+ error_format: ErrorOutputType,
+ mut codegen_units: Option<usize>,
+) -> (bool, Option<usize>) {
+ let mut disable_thinlto = false;
+ // Issue #30063: if user requests LLVM-related output to one
+ // particular path, disable codegen-units.
+ let incompatible: Vec<_> = output_types
+ .0
+ .iter()
+ .map(|ot_path| ot_path.0)
+ .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file())
+ .map(|ot| ot.shorthand())
+ .collect();
+ if !incompatible.is_empty() {
+ match codegen_units {
+ Some(n) if n > 1 => {
+ if matches.opt_present("o") {
+ for ot in &incompatible {
+ early_warn(
+ error_format,
+ &format!(
+ "`--emit={ot}` with `-o` incompatible with \
+ `-C codegen-units=N` for N > 1",
+ ),
+ );
+ }
+ early_warn(error_format, "resetting to default -C codegen-units=1");
+ codegen_units = Some(1);
+ disable_thinlto = true;
+ }
+ }
+ _ => {
+ codegen_units = Some(1);
+ disable_thinlto = true;
+ }
+ }
+ }
+
+ if codegen_units == Some(0) {
+ early_error(error_format, "value for codegen units must be a positive non-zero integer");
+ }
+
+ (disable_thinlto, codegen_units)
+}
+
+fn check_thread_count(unstable_opts: &UnstableOptions, error_format: ErrorOutputType) {
+ if unstable_opts.threads == 0 {
+ early_error(error_format, "value for threads must be a positive non-zero integer");
+ }
+
+ if unstable_opts.threads > 1 && unstable_opts.fuel.is_some() {
+ early_error(error_format, "optimization fuel is incompatible with multiple threads");
+ }
+}
+
+fn collect_print_requests(
+ cg: &mut CodegenOptions,
+ unstable_opts: &mut UnstableOptions,
+ matches: &getopts::Matches,
+ error_format: ErrorOutputType,
+) -> Vec<PrintRequest> {
+ let mut prints = Vec::<PrintRequest>::new();
+ if cg.target_cpu.as_ref().map_or(false, |s| s == "help") {
+ prints.push(PrintRequest::TargetCPUs);
+ cg.target_cpu = None;
+ };
+ if cg.target_feature == "help" {
+ prints.push(PrintRequest::TargetFeatures);
+ cg.target_feature = String::new();
+ }
+
+ prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s {
+ "crate-name" => PrintRequest::CrateName,
+ "file-names" => PrintRequest::FileNames,
+ "sysroot" => PrintRequest::Sysroot,
+ "target-libdir" => PrintRequest::TargetLibdir,
+ "cfg" => PrintRequest::Cfg,
+ "target-list" => PrintRequest::TargetList,
+ "target-cpus" => PrintRequest::TargetCPUs,
+ "target-features" => PrintRequest::TargetFeatures,
+ "relocation-models" => PrintRequest::RelocationModels,
+ "code-models" => PrintRequest::CodeModels,
+ "tls-models" => PrintRequest::TlsModels,
+ "native-static-libs" => PrintRequest::NativeStaticLibs,
+ "stack-protector-strategies" => PrintRequest::StackProtectorStrategies,
+ "target-spec-json" => {
+ if unstable_opts.unstable_options {
+ PrintRequest::TargetSpec
+ } else {
+ early_error(
+ error_format,
+ "the `-Z unstable-options` flag must also be passed to \
+ enable the target-spec-json print option",
+ );
+ }
+ }
+ "link-args" => PrintRequest::LinkArgs,
+ req => early_error(error_format, &format!("unknown print request `{req}`")),
+ }));
+
+ prints
+}
+
+pub fn parse_target_triple(
+ matches: &getopts::Matches,
+ error_format: ErrorOutputType,
+) -> TargetTriple {
+ match matches.opt_str("target") {
+ Some(target) if target.ends_with(".json") => {
+ let path = Path::new(&target);
+ TargetTriple::from_path(&path).unwrap_or_else(|_| {
+ early_error(error_format, &format!("target file {path:?} does not exist"))
+ })
+ }
+ Some(target) => TargetTriple::TargetTriple(target),
+ _ => TargetTriple::from_triple(host_triple()),
+ }
+}
+
+fn parse_opt_level(
+ matches: &getopts::Matches,
+ cg: &CodegenOptions,
+ error_format: ErrorOutputType,
+) -> OptLevel {
+ // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able
+ // to use them interchangeably. However, because they're technically different flags,
+ // we need to work out manually which should take precedence if both are supplied (i.e.
+ // the rightmost flag). We do this by finding the (rightmost) position of both flags and
+ // comparing them. Note that if a flag is not found, its position will be `None`, which
+ // always compared less than `Some(_)`.
+ let max_o = matches.opt_positions("O").into_iter().max();
+ let max_c = matches
+ .opt_strs_pos("C")
+ .into_iter()
+ .flat_map(|(i, s)| {
+ // NB: This can match a string without `=`.
+ if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None }
+ })
+ .max();
+ if max_o > max_c {
+ OptLevel::Default
+ } else {
+ match cg.opt_level.as_ref() {
+ "0" => OptLevel::No,
+ "1" => OptLevel::Less,
+ "2" => OptLevel::Default,
+ "3" => OptLevel::Aggressive,
+ "s" => OptLevel::Size,
+ "z" => OptLevel::SizeMin,
+ arg => {
+ early_error(
+ error_format,
+ &format!(
+ "optimization level needs to be \
+ between 0-3, s or z (instead was `{arg}`)"
+ ),
+ );
+ }
+ }
+ }
+}
+
+fn select_debuginfo(
+ matches: &getopts::Matches,
+ cg: &CodegenOptions,
+ error_format: ErrorOutputType,
+) -> DebugInfo {
+ let max_g = matches.opt_positions("g").into_iter().max();
+ let max_c = matches
+ .opt_strs_pos("C")
+ .into_iter()
+ .flat_map(|(i, s)| {
+ // NB: This can match a string without `=`.
+ if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None }
+ })
+ .max();
+ if max_g > max_c {
+ DebugInfo::Full
+ } else {
+ match cg.debuginfo {
+ 0 => DebugInfo::None,
+ 1 => DebugInfo::Limited,
+ 2 => DebugInfo::Full,
+ arg => {
+ early_error(
+ error_format,
+ &format!(
+ "debug info level needs to be between \
+ 0-2 (instead was `{arg}`)"
+ ),
+ );
+ }
+ }
+ }
+}
+
+pub(crate) fn parse_assert_incr_state(
+ opt_assertion: &Option<String>,
+ error_format: ErrorOutputType,
+) -> 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_error(error_format, &format!("unexpected incremental state assertion value: {s}"))
+ }
+ None => None,
+ }
+}
+
+fn parse_native_lib_kind(
+ matches: &getopts::Matches,
+ kind: &str,
+ error_format: ErrorOutputType,
+) -> (NativeLibKind, Option<bool>) {
+ let (kind, modifiers) = match kind.split_once(':') {
+ None => (kind, None),
+ Some((kind, modifiers)) => (kind, Some(modifiers)),
+ };
+
+ let kind = match kind {
+ "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
+ "dylib" => NativeLibKind::Dylib { as_needed: None },
+ "framework" => NativeLibKind::Framework { as_needed: None },
+ "link-arg" => {
+ if !nightly_options::is_unstable_enabled(matches) {
+ let why = if nightly_options::match_is_nightly_build(matches) {
+ " and only accepted on the nightly compiler"
+ } else {
+ ", the `-Z unstable-options` flag must also be passed to use it"
+ };
+ early_error(error_format, &format!("library kind `link-arg` is unstable{why}"))
+ }
+ NativeLibKind::LinkArg
+ }
+ _ => early_error(
+ error_format,
+ &format!(
+ "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
+ ),
+ ),
+ };
+ match modifiers {
+ None => (kind, None),
+ Some(modifiers) => parse_native_lib_modifiers(kind, modifiers, error_format, matches),
+ }
+}
+
+fn parse_native_lib_modifiers(
+ mut kind: NativeLibKind,
+ modifiers: &str,
+ error_format: ErrorOutputType,
+ matches: &getopts::Matches,
+) -> (NativeLibKind, Option<bool>) {
+ let mut verbatim = None;
+ for modifier in modifiers.split(',') {
+ let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
+ Some(m) => (m, modifier.starts_with('+')),
+ None => early_error(
+ error_format,
+ "invalid linking modifier syntax, expected '+' or '-' prefix \
+ before one of: bundle, verbatim, whole-archive, as-needed",
+ ),
+ };
+
+ let report_unstable_modifier = || {
+ if !nightly_options::is_unstable_enabled(matches) {
+ let why = if nightly_options::match_is_nightly_build(matches) {
+ " and only accepted on the nightly compiler"
+ } else {
+ ", the `-Z unstable-options` flag must also be passed to use it"
+ };
+ early_error(
+ error_format,
+ &format!("linking modifier `{modifier}` is unstable{why}"),
+ )
+ }
+ };
+ let assign_modifier = |dst: &mut Option<bool>| {
+ if dst.is_some() {
+ let msg = format!("multiple `{modifier}` modifiers in a single `-l` option");
+ early_error(error_format, &msg)
+ } else {
+ *dst = Some(value);
+ }
+ };
+ match (modifier, &mut kind) {
+ ("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle),
+ ("bundle", _) => early_error(
+ error_format,
+ "linking modifier `bundle` is only compatible with `static` linking kind",
+ ),
+
+ ("verbatim", _) => {
+ report_unstable_modifier();
+ assign_modifier(&mut verbatim)
+ }
+
+ ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
+ assign_modifier(whole_archive)
+ }
+ ("whole-archive", _) => early_error(
+ error_format,
+ "linking modifier `whole-archive` is only compatible with `static` linking kind",
+ ),
+
+ ("as-needed", NativeLibKind::Dylib { as_needed })
+ | ("as-needed", NativeLibKind::Framework { as_needed }) => {
+ report_unstable_modifier();
+ assign_modifier(as_needed)
+ }
+ ("as-needed", _) => early_error(
+ error_format,
+ "linking modifier `as-needed` is only compatible with \
+ `dylib` and `framework` linking kinds",
+ ),
+
+ // Note: this error also excludes the case with empty modifier
+ // string, like `modifiers = ""`.
+ _ => early_error(
+ error_format,
+ &format!(
+ "unknown linking modifier `{modifier}`, expected one \
+ of: bundle, verbatim, whole-archive, as-needed"
+ ),
+ ),
+ }
+ }
+
+ (kind, verbatim)
+}
+
+fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<NativeLib> {
+ matches
+ .opt_strs("l")
+ .into_iter()
+ .map(|s| {
+ // Parse string of the form "[KIND[:MODIFIERS]=]lib[:new_name]",
+ // where KIND is one of "dylib", "framework", "static", "link-arg" and
+ // where MODIFIERS are a comma separated list of supported modifiers
+ // (bundle, verbatim, whole-archive, as-needed). Each modifier is prefixed
+ // with either + or - to indicate whether it is enabled or disabled.
+ // The last value specified for a given modifier wins.
+ let (name, kind, verbatim) = match s.split_once('=') {
+ None => (s, NativeLibKind::Unspecified, None),
+ Some((kind, name)) => {
+ let (kind, verbatim) = parse_native_lib_kind(matches, kind, error_format);
+ (name.to_string(), kind, verbatim)
+ }
+ };
+
+ let (name, new_name) = match name.split_once(':') {
+ None => (name, None),
+ Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
+ };
+ if name.is_empty() {
+ early_error(error_format, "library name must not be empty");
+ }
+ NativeLib { name, new_name, kind, verbatim }
+ })
+ .collect()
+}
+
+pub fn parse_externs(
+ matches: &getopts::Matches,
+ unstable_opts: &UnstableOptions,
+ error_format: ErrorOutputType,
+) -> 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 (name, path) = match arg.split_once('=') {
+ None => (arg, None),
+ Some((name, path)) => (name.to_string(), Some(Path::new(path))),
+ };
+ let (options, name) = match name.split_once(':') {
+ None => (None, name),
+ Some((opts, name)) => (Some(opts), name.to_string()),
+ };
+
+ let path = path.map(|p| CanonicalizedPath::new(p));
+
+ 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
+ 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;
+ if let Some(opts) = options {
+ if !is_unstable_enabled {
+ early_error(
+ error_format,
+ "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_error(
+ error_format,
+ "the `noprelude` --extern option requires a file path",
+ );
+ }
+ }
+ "nounused" => nounused_dep = true,
+ _ => early_error(error_format, &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;
+ // If any flag is missing `noprelude`, then add to the prelude.
+ entry.add_prelude |= add_prelude;
+ }
+ Externs(externs)
+}
+
+fn parse_remap_path_prefix(
+ matches: &getopts::Matches,
+ unstable_opts: &UnstableOptions,
+ error_format: ErrorOutputType,
+) -> 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_error(
+ error_format,
+ "--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
+}
+
+// JUSTIFICATION: before wrapper fn is available
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+pub fn build_session_options(matches: &getopts::Matches) -> Options {
+ let color = parse_color(matches);
+
+ let edition = parse_crate_edition(matches);
+
+ let JsonConfig {
+ json_rendered,
+ json_artifact_notifications,
+ json_unused_externs,
+ json_future_incompat,
+ } = parse_json(matches);
+
+ let error_format = parse_error_format(matches, color, json_rendered);
+
+ let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| {
+ early_error(error_format, "`--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_error(error_format, &e));
+
+ let mut unstable_opts = UnstableOptions::build(matches, error_format);
+ let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
+
+ check_error_format_stability(&unstable_opts, error_format, json_rendered);
+
+ if !unstable_opts.unstable_options && json_unused_externs.is_enabled() {
+ early_error(
+ error_format,
+ "the `-Z unstable-options` flag must also be passed to enable \
+ the flag `--json=unused-externs`",
+ );
+ }
+
+ let output_types = parse_output_types(&unstable_opts, matches, error_format);
+
+ let mut cg = CodegenOptions::build(matches, error_format);
+ let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto(
+ &output_types,
+ matches,
+ error_format,
+ cg.codegen_units,
+ );
+
+ check_thread_count(&unstable_opts, error_format);
+
+ let incremental = cg.incremental.as_ref().map(PathBuf::from);
+
+ let assert_incr_state = parse_assert_incr_state(&unstable_opts.assert_incr_state, error_format);
+
+ if unstable_opts.profile && incremental.is_some() {
+ early_error(
+ error_format,
+ "can't instrument with gcov profiling when compiling incrementally",
+ );
+ }
+ if unstable_opts.profile {
+ match codegen_units {
+ Some(1) => {}
+ None => codegen_units = Some(1),
+ Some(_) => early_error(
+ error_format,
+ "can't instrument with gcov profiling with multiple codegen units",
+ ),
+ }
+ }
+
+ if cg.profile_generate.enabled() && cg.profile_use.is_some() {
+ early_error(
+ error_format,
+ "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_error(
+ error_format,
+ "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`",
+ );
+ }
+
+ // Handle both `-Z symbol-mangling-version` and `-C symbol-mangling-version`; the latter takes
+ // precedence.
+ match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) {
+ (Some(smv_c), Some(smv_z)) if smv_c != smv_z => {
+ early_error(
+ error_format,
+ "incompatible values passed for `-C symbol-mangling-version` \
+ and `-Z symbol-mangling-version`",
+ );
+ }
+ (Some(SymbolManglingVersion::V0), _) => {}
+ (Some(_), _) if !unstable_opts.unstable_options => {
+ early_error(
+ error_format,
+ "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
+ );
+ }
+ (None, None) => {}
+ (None, smv) => {
+ early_warn(
+ error_format,
+ "`-Z symbol-mangling-version` is deprecated; use `-C symbol-mangling-version`",
+ );
+ cg.symbol_mangling_version = smv;
+ }
+ _ => {}
+ }
+
+ // Handle both `-Z instrument-coverage` and `-C instrument-coverage`; the latter takes
+ // precedence.
+ match (cg.instrument_coverage, unstable_opts.instrument_coverage) {
+ (Some(ic_c), Some(ic_z)) if ic_c != ic_z => {
+ early_error(
+ error_format,
+ "incompatible values passed for `-C instrument-coverage` \
+ and `-Z instrument-coverage`",
+ );
+ }
+ (Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {}
+ (Some(_), _) if !unstable_opts.unstable_options => {
+ early_error(
+ error_format,
+ "`-C instrument-coverage=except-*` requires `-Z unstable-options`",
+ );
+ }
+ (None, None) => {}
+ (None, ic) => {
+ early_warn(
+ error_format,
+ "`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`",
+ );
+ cg.instrument_coverage = ic;
+ }
+ _ => {}
+ }
+
+ if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) {
+ if cg.profile_generate.enabled() || cg.profile_use.is_some() {
+ early_error(
+ error_format,
+ "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_warn(
+ error_format,
+ "-C instrument-coverage requires symbol mangling version `v0`, \
+ but `-C symbol-mangling-version=legacy` was specified",
+ );
+ }
+ Some(SymbolManglingVersion::V0) => {}
+ }
+ }
+
+ if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") {
+ 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_error(
+ error_format,
+ "options `-C embed-bitcode=no` and `-C lto` are incompatible",
+ ),
+ }
+ }
+
+ if cg.linker_flavor == Some(LinkerFlavor::L4Bender)
+ && !nightly_options::is_unstable_enabled(matches)
+ {
+ early_error(
+ error_format,
+ "`l4-bender` linker flavor is unstable, `-Z unstable-options` \
+ flag must also be passed to explicitly use it",
+ );
+ }
+
+ let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format);
+
+ let cg = cg;
+
+ let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
+ let target_triple = parse_target_triple(matches, error_format);
+ let opt_level = parse_opt_level(matches, &cg, error_format);
+ // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able
+ // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`)
+ // for more details.
+ let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
+ let debuginfo = select_debuginfo(matches, &cg, error_format);
+
+ let mut search_paths = vec![];
+ for s in &matches.opt_strs("L") {
+ search_paths.push(SearchPath::from_cli_opt(&s, error_format));
+ }
+
+ let libs = parse_libs(matches, error_format);
+
+ let test = matches.opt_present("test");
+
+ if !cg.remark.is_empty() && debuginfo == DebugInfo::None {
+ early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations");
+ }
+
+ let externs = parse_externs(matches, &unstable_opts, error_format);
+
+ let crate_name = matches.opt_str("crate-name");
+
+ let remap_path_prefix = parse_remap_path_prefix(matches, &unstable_opts, error_format);
+
+ let pretty = parse_pretty(&unstable_opts, error_format);
+
+ if !unstable_opts.unstable_options
+ && !target_triple.triple().contains("apple")
+ && cg.split_debuginfo.is_some()
+ {
+ early_error(error_format, "`-Csplit-debuginfo` is unstable on this platform");
+ }
+
+ // Try to find a directory containing the Rust `src`, for more details see
+ // the doc comment on the `real_rust_source_base_dir` field.
+ let tmp_buf;
+ let sysroot = match &sysroot_opt {
+ Some(s) => s,
+ None => {
+ tmp_buf = crate::filesearch::get_or_default_sysroot();
+ &tmp_buf
+ }
+ };
+ let real_rust_source_base_dir = {
+ // This is the location used by the `rust-src` `rustup` component.
+ let mut candidate = sysroot.join("lib/rustlib/src/rust");
+ if let Ok(metadata) = candidate.symlink_metadata() {
+ // Replace the symlink rustbuild 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.
+ if candidate.join("library/std/src/lib.rs").is_file() { Some(candidate) } else { None }
+ };
+
+ let working_dir = std::env::current_dir().unwrap_or_else(|e| {
+ early_error(error_format, &format!("Current directory is invalid: {e}"));
+ });
+
+ let (path, remapped) =
+ FilePathMapping::new(remap_path_prefix.clone()).map_prefix(working_dir.clone());
+ let working_dir = if remapped {
+ RealFileName::Remapped { local_path: Some(working_dir), virtual_name: path }
+ } else {
+ RealFileName::LocalPath(path)
+ };
+
+ Options {
+ assert_incr_state,
+ crate_types,
+ optimize: opt_level,
+ debuginfo,
+ lint_opts,
+ lint_cap,
+ describe_lints,
+ output_types,
+ search_paths,
+ maybe_sysroot: sysroot_opt,
+ target_triple,
+ test,
+ incremental,
+ unstable_opts,
+ prints,
+ cg,
+ error_format,
+ diagnostic_width,
+ externs,
+ unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()),
+ crate_name,
+ libs,
+ debug_assertions,
+ actually_rustdoc: false,
+ trimmed_def_paths: TrimmedDefPaths::default(),
+ cli_forced_codegen_units: codegen_units,
+ cli_forced_thinlto_off: disable_thinlto,
+ remap_path_prefix,
+ real_rust_source_base_dir,
+ edition,
+ json_artifact_notifications,
+ json_unused_externs,
+ json_future_incompat,
+ pretty,
+ working_dir,
+ }
+}
+
+fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> 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(PpAstTreeMode::Normal),
+ "ast-tree,expanded" => AstTree(PpAstTreeMode::Expanded),
+ "hir" => Hir(PpHirMode::Normal),
+ "hir,identified" => Hir(PpHirMode::Identified),
+ "hir,typed" => Hir(PpHirMode::Typed),
+ "hir-tree" => HirTree,
+ "thir-tree" => ThirTree,
+ "mir" => Mir,
+ "mir-cfg" => MirCFG,
+ name => early_error(
+ efmt,
+ &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`, `mir` or `mir-cfg`; got {name}"
+ ),
+ ),
+ };
+ tracing::debug!("got unpretty option: {first:?}");
+ Some(first)
+}
+
+pub fn make_crate_type_option() -> RustcOptGroup {
+ opt::multi_s(
+ "",
+ "crate-type",
+ "Comma separated list of types of crates
+ for the compiler to emit",
+ "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]",
+ )
+}
+
+pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> {
+ let mut crate_types: Vec<CrateType> = Vec::new();
+ for unparsed_crate_type in &list_list {
+ for part in unparsed_crate_type.split(',') {
+ let new_part = match part {
+ "lib" => default_lib_output(),
+ "rlib" => CrateType::Rlib,
+ "staticlib" => CrateType::Staticlib,
+ "dylib" => CrateType::Dylib,
+ "cdylib" => CrateType::Cdylib,
+ "bin" => CrateType::Executable,
+ "proc-macro" => CrateType::ProcMacro,
+ _ => return Err(format!("unknown crate type: `{part}`")),
+ };
+ if !crate_types.contains(&new_part) {
+ crate_types.push(new_part)
+ }
+ }
+ }
+
+ Ok(crate_types)
+}
+
+pub mod nightly_options {
+ use super::{ErrorOutputType, OptionStability, RustcOptGroup};
+ use crate::early_error;
+ use rustc_feature::UnstableFeatures;
+
+ 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())
+ }
+
+ pub fn is_nightly_build(krate: Option<&str>) -> bool {
+ UnstableFeatures::from_environment(krate).is_nightly_build()
+ }
+
+ pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) {
+ let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
+ let really_allows_unstable_options = match_is_nightly_build(matches);
+
+ for opt in flags.iter() {
+ if opt.stability == OptionStability::Stable {
+ continue;
+ }
+ if !matches.opt_present(opt.name) {
+ continue;
+ }
+ if opt.name != "Z" && !has_z_unstable_option {
+ early_error(
+ ErrorOutputType::default(),
+ &format!(
+ "the `-Z unstable-options` flag must also be passed to enable \
+ the flag `{}`",
+ opt.name
+ ),
+ );
+ }
+ if really_allows_unstable_options {
+ continue;
+ }
+ match opt.stability {
+ OptionStability::Unstable => {
+ let msg = format!(
+ "the option `{}` is only accepted on the \
+ nightly compiler",
+ opt.name
+ );
+ early_error(ErrorOutputType::default(), &msg);
+ }
+ OptionStability::Stable => {}
+ }
+ }
+ }
+}
+
+impl fmt::Display for CrateType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ CrateType::Executable => "bin".fmt(f),
+ CrateType::Dylib => "dylib".fmt(f),
+ CrateType::Rlib => "rlib".fmt(f),
+ CrateType::Staticlib => "staticlib".fmt(f),
+ CrateType::Cdylib => "cdylib".fmt(f),
+ CrateType::ProcMacro => "proc-macro".fmt(f),
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum PpSourceMode {
+ /// `-Zunpretty=normal`
+ Normal,
+ /// `-Zunpretty=expanded`
+ Expanded,
+ /// `-Zunpretty=identified`
+ Identified,
+ /// `-Zunpretty=expanded,identified`
+ ExpandedIdentified,
+ /// `-Zunpretty=expanded,hygiene`
+ ExpandedHygiene,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum PpAstTreeMode {
+ /// `-Zunpretty=ast`
+ Normal,
+ /// `-Zunpretty=ast,expanded`
+ Expanded,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum PpHirMode {
+ /// `-Zunpretty=hir`
+ Normal,
+ /// `-Zunpretty=hir,identified`
+ Identified,
+ /// `-Zunpretty=hir,typed`
+ Typed,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum PpMode {
+ /// Options that print the source code, i.e.
+ /// `-Zunpretty=normal` and `-Zunpretty=expanded`
+ Source(PpSourceMode),
+ AstTree(PpAstTreeMode),
+ /// Options that print the HIR, i.e. `-Zunpretty=hir`
+ Hir(PpHirMode),
+ /// `-Zunpretty=hir-tree`
+ HirTree,
+ /// `-Zunpretty=thir-tree`
+ ThirTree,
+ /// `-Zunpretty=mir`
+ Mir,
+ /// `-Zunpretty=mir-cfg`
+ MirCFG,
+}
+
+impl PpMode {
+ pub fn needs_ast_map(&self) -> bool {
+ use PpMode::*;
+ use PpSourceMode::*;
+ match *self {
+ Source(Normal | Identified) | AstTree(PpAstTreeMode::Normal) => false,
+
+ Source(Expanded | ExpandedIdentified | ExpandedHygiene)
+ | AstTree(PpAstTreeMode::Expanded)
+ | Hir(_)
+ | HirTree
+ | ThirTree
+ | Mir
+ | MirCFG => true,
+ }
+ }
+ pub fn needs_hir(&self) -> bool {
+ use PpMode::*;
+ match *self {
+ Source(_) | AstTree(_) => false,
+
+ Hir(_) | HirTree | ThirTree | Mir | MirCFG => true,
+ }
+ }
+
+ pub fn needs_analysis(&self) -> bool {
+ use PpMode::*;
+ matches!(*self, Mir | MirCFG | ThirTree)
+ }
+}
+
+/// 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 super::{
+ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType,
+ InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OomStrategy, OptLevel,
+ OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SplitDwarfKind,
+ SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths,
+ };
+ use crate::lint;
+ use crate::options::WasiExecModel;
+ use crate::utils::{NativeLib, NativeLibKind};
+ use rustc_errors::LanguageIdentifier;
+ use rustc_feature::UnstableFeatures;
+ use rustc_span::edition::Edition;
+ use rustc_span::RealFileName;
+ use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
+ use rustc_target::spec::{
+ RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
+ };
+ use std::collections::hash_map::DefaultHasher;
+ use std::collections::BTreeMap;
+ use std::hash::Hash;
+ use std::num::NonZeroUsize;
+ use std::path::PathBuf;
+
+ pub trait DepTrackingHash {
+ fn hash(
+ &self,
+ hasher: &mut DefaultHasher,
+ 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 DefaultHasher, _: ErrorOutputType, _for_crate_hash: bool) {
+ Hash::hash(self, hasher);
+ }
+ }
+ )+};
+ }
+
+ impl<T: DepTrackingHash> DepTrackingHash for Option<T> {
+ fn hash(
+ &self,
+ hasher: &mut DefaultHasher,
+ 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!(
+ bool,
+ usize,
+ NonZeroUsize,
+ u64,
+ String,
+ PathBuf,
+ lint::Level,
+ WasiExecModel,
+ u32,
+ RelocModel,
+ CodeModel,
+ TlsModel,
+ InstrumentCoverage,
+ CrateType,
+ MergeFunctions,
+ PanicStrategy,
+ RelroLevel,
+ Passes,
+ OptLevel,
+ LtoCli,
+ DebugInfo,
+ UnstableFeatures,
+ NativeLib,
+ NativeLibKind,
+ SanitizerSet,
+ CFGuard,
+ CFProtection,
+ TargetTriple,
+ Edition,
+ LinkerPluginLto,
+ SplitDebuginfo,
+ SplitDwarfKind,
+ StackProtector,
+ SwitchWithOptPath,
+ SymbolManglingVersion,
+ SourceFileHashAlgorithm,
+ TrimmedDefPaths,
+ Option<LdImpl>,
+ OutputType,
+ RealFileName,
+ LocationDetail,
+ BranchProtection,
+ OomStrategy,
+ LanguageIdentifier,
+ );
+
+ impl<T1, T2> DepTrackingHash for (T1, T2)
+ where
+ T1: DepTrackingHash,
+ T2: DepTrackingHash,
+ {
+ fn hash(
+ &self,
+ hasher: &mut DefaultHasher,
+ 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 DefaultHasher,
+ 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 DefaultHasher,
+ 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 DepTrackingHash for OutputTypes {
+ fn hash(
+ &self,
+ hasher: &mut DefaultHasher,
+ 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 DefaultHasher,
+ error_format: ErrorOutputType,
+ for_crate_hash: bool,
+ ) {
+ for (key, sub_hash) in sub_hashes {
+ // Using Hash::hash() instead of DepTrackingHash::hash() is fine for
+ // the keys, as they are just plain strings
+ Hash::hash(&key.len(), hasher);
+ Hash::hash(key, hasher);
+ sub_hash.hash(hasher, error_format, for_crate_hash);
+ }
+ }
+}
+
+/// Default behavior to use in out-of-memory situations.
+#[derive(Clone, Copy, PartialEq, Hash, Debug, Encodable, Decodable, HashStable_Generic)]
+pub enum OomStrategy {
+ /// Generate a panic that can be caught by `catch_unwind`.
+ Panic,
+
+ /// Abort the process immediately.
+ Abort,
+}
+
+impl OomStrategy {
+ pub const SYMBOL: &'static str = "__rust_alloc_error_handler_should_panic";
+
+ pub fn should_panic(self) -> u8 {
+ match self {
+ OomStrategy::Panic => 1,
+ OomStrategy::Abort => 0,
+ }
+ }
+}
+
+/// How to run proc-macro code when building this crate
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum ProcMacroExecutionStrategy {
+ /// Run the proc-macro code on the same thread as the server.
+ SameThread,
+
+ /// Run the proc-macro code on a different thread.
+ CrossThread,
+}
diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs
new file mode 100644
index 000000000..c1fd3c7c6
--- /dev/null
+++ b/compiler/rustc_session/src/cstore.rs
@@ -0,0 +1,218 @@
+//! the rustc crate store interface. This also includes types that
+//! are *mostly* used as a part of that interface, but these should
+//! probably get a better home if someone can find one.
+
+use crate::search_paths::PathKind;
+use crate::utils::NativeLibKind;
+use crate::Session;
+use rustc_ast as ast;
+use rustc_data_structures::sync::{self, MetadataRef};
+use rustc_hir::def_id::{CrateNum, DefId, StableCrateId, LOCAL_CRATE};
+use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
+use rustc_span::hygiene::{ExpnHash, ExpnId};
+use rustc_span::symbol::Symbol;
+use rustc_span::Span;
+use rustc_target::spec::Target;
+
+use std::any::Any;
+use std::path::{Path, PathBuf};
+
+// lonely orphan structs and enums looking for a better home
+
+/// Where a crate came from on the local filesystem. One of these three options
+/// must be non-None.
+#[derive(PartialEq, Clone, Debug, HashStable_Generic, Encodable, Decodable)]
+pub struct CrateSource {
+ pub dylib: Option<(PathBuf, PathKind)>,
+ pub rlib: Option<(PathBuf, PathKind)>,
+ pub rmeta: Option<(PathBuf, PathKind)>,
+}
+
+impl CrateSource {
+ #[inline]
+ pub fn paths(&self) -> impl Iterator<Item = &PathBuf> {
+ self.dylib.iter().chain(self.rlib.iter()).chain(self.rmeta.iter()).map(|p| &p.0)
+ }
+}
+
+#[derive(Encodable, Decodable, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
+#[derive(HashStable_Generic)]
+pub enum CrateDepKind {
+ /// A dependency that is only used for its macros.
+ MacrosOnly,
+ /// A dependency that is always injected into the dependency list and so
+ /// doesn't need to be linked to an rlib, e.g., the injected allocator.
+ Implicit,
+ /// A dependency that is required by an rlib version of this crate.
+ /// Ordinary `extern crate`s result in `Explicit` dependencies.
+ Explicit,
+}
+
+impl CrateDepKind {
+ #[inline]
+ pub fn macros_only(self) -> bool {
+ match self {
+ CrateDepKind::MacrosOnly => true,
+ CrateDepKind::Implicit | CrateDepKind::Explicit => false,
+ }
+ }
+}
+
+#[derive(Copy, Debug, PartialEq, Clone, Encodable, Decodable, HashStable_Generic)]
+pub enum LinkagePreference {
+ RequireDynamic,
+ RequireStatic,
+}
+
+#[derive(Debug, Encodable, Decodable, HashStable_Generic)]
+pub struct NativeLib {
+ pub kind: NativeLibKind,
+ pub name: Option<Symbol>,
+ pub cfg: Option<ast::MetaItem>,
+ pub foreign_module: Option<DefId>,
+ pub wasm_import_module: Option<Symbol>,
+ pub verbatim: Option<bool>,
+ pub dll_imports: Vec<DllImport>,
+}
+
+impl NativeLib {
+ pub fn has_modifiers(&self) -> bool {
+ self.verbatim.is_some() || self.kind.has_modifiers()
+ }
+}
+
+#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)]
+pub struct DllImport {
+ pub name: Symbol,
+ pub ordinal: Option<u16>,
+ /// Calling convention for the function.
+ ///
+ /// On x86_64, this is always `DllCallingConvention::C`; on i686, it can be any
+ /// of the values, and we use `DllCallingConvention::C` to represent `"cdecl"`.
+ pub calling_convention: DllCallingConvention,
+ /// Span of import's "extern" declaration; used for diagnostics.
+ pub span: Span,
+}
+
+/// Calling convention for a function defined in an external library.
+///
+/// The usize value, where present, indicates the size of the function's argument list
+/// in bytes.
+#[derive(Clone, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)]
+pub enum DllCallingConvention {
+ C,
+ Stdcall(usize),
+ Fastcall(usize),
+ Vectorcall(usize),
+}
+
+#[derive(Clone, Encodable, Decodable, HashStable_Generic, Debug)]
+pub struct ForeignModule {
+ pub foreign_items: Vec<DefId>,
+ pub def_id: DefId,
+}
+
+#[derive(Copy, Clone, Debug, HashStable_Generic)]
+pub struct ExternCrate {
+ pub src: ExternCrateSource,
+
+ /// span of the extern crate that caused this to be loaded
+ pub span: Span,
+
+ /// Number of links to reach the extern;
+ /// used to select the extern with the shortest path
+ pub path_len: usize,
+
+ /// Crate that depends on this crate
+ pub dependency_of: CrateNum,
+}
+
+impl ExternCrate {
+ /// If true, then this crate is the crate named by the extern
+ /// crate referenced above. If false, then this crate is a dep
+ /// of the crate.
+ #[inline]
+ pub fn is_direct(&self) -> bool {
+ self.dependency_of == LOCAL_CRATE
+ }
+
+ #[inline]
+ pub fn rank(&self) -> impl PartialOrd {
+ // Prefer:
+ // - direct extern crate to indirect
+ // - shorter paths to longer
+ (self.is_direct(), !self.path_len)
+ }
+}
+
+#[derive(Copy, Clone, Debug, HashStable_Generic)]
+pub enum ExternCrateSource {
+ /// Crate is loaded by `extern crate`.
+ Extern(
+ /// def_id of the item in the current crate that caused
+ /// this crate to be loaded; note that there could be multiple
+ /// such ids
+ DefId,
+ ),
+ /// Crate is implicitly loaded by a path resolving through extern prelude.
+ Path,
+}
+
+/// The backend's way to give the crate store access to the metadata in a library.
+/// Note that it returns the raw metadata bytes stored in the library file, whether
+/// it is compressed, uncompressed, some weird mix, etc.
+/// rmeta files are backend independent and not handled here.
+///
+/// At the time of this writing, there is only one backend and one way to store
+/// metadata in library -- this trait just serves to decouple rustc_metadata from
+/// the archive reader, which depends on LLVM.
+pub trait MetadataLoader {
+ fn get_rlib_metadata(&self, target: &Target, filename: &Path) -> Result<MetadataRef, String>;
+ fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<MetadataRef, String>;
+}
+
+pub type MetadataLoaderDyn = dyn MetadataLoader + Sync;
+
+/// A store of Rust crates, through which their metadata can be accessed.
+///
+/// Note that this trait should probably not be expanding today. All new
+/// functionality should be driven through queries instead!
+///
+/// If you find a method on this trait named `{name}_untracked` it signifies
+/// that it's *not* tracked for dependency information throughout compilation
+/// (it'd break incremental compilation) and should only be called pre-HIR (e.g.
+/// during resolve)
+pub trait CrateStore: std::fmt::Debug {
+ fn as_any(&self) -> &dyn Any;
+
+ // Foreign definitions.
+ // This information is safe to access, since it's hashed as part of the DefPathHash, which incr.
+ // comp. uses to identify a DefId.
+ fn def_key(&self, def: DefId) -> DefKey;
+ fn def_path(&self, def: DefId) -> DefPath;
+ fn def_path_hash(&self, def: DefId) -> DefPathHash;
+
+ // This information is safe to access, since it's hashed as part of the StableCrateId, which
+ // incr. comp. uses to identify a CrateNum.
+ fn crate_name(&self, cnum: CrateNum) -> Symbol;
+ fn stable_crate_id(&self, cnum: CrateNum) -> StableCrateId;
+ fn stable_crate_id_to_crate_num(&self, stable_crate_id: StableCrateId) -> CrateNum;
+
+ /// Fetch a DefId from a DefPathHash for a foreign crate.
+ fn def_path_hash_to_def_id(&self, cnum: CrateNum, hash: DefPathHash) -> DefId;
+ fn expn_hash_to_expn_id(
+ &self,
+ sess: &Session,
+ cnum: CrateNum,
+ index_guess: u32,
+ hash: ExpnHash,
+ ) -> ExpnId;
+
+ /// Imports all `SourceFile`s from the given crate into the current session.
+ /// This normally happens automatically when we decode a `Span` from
+ /// that crate's metadata - however, the incr comp cache needs
+ /// to trigger this manually when decoding a foreign `Span`
+ fn import_source_files(&self, sess: &Session, cnum: CrateNum);
+}
+
+pub type CrateStoreDyn = dyn CrateStore + sync::Sync;
diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs
new file mode 100644
index 000000000..c973e3140
--- /dev/null
+++ b/compiler/rustc_session/src/filesearch.rs
@@ -0,0 +1,125 @@
+//! A module for searching for libraries
+
+use std::env;
+use std::fs;
+use std::iter::FromIterator;
+use std::path::{Path, PathBuf};
+
+use crate::search_paths::{PathKind, SearchPath};
+use rustc_fs_util::fix_windows_verbatim_for_gcc;
+use tracing::debug;
+
+#[derive(Copy, Clone)]
+pub enum FileMatch {
+ FileMatches,
+ FileDoesntMatch,
+}
+
+#[derive(Clone)]
+pub struct FileSearch<'a> {
+ sysroot: &'a Path,
+ triple: &'a str,
+ search_paths: &'a [SearchPath],
+ tlib_path: &'a SearchPath,
+ kind: PathKind,
+}
+
+impl<'a> FileSearch<'a> {
+ pub fn search_paths(&self) -> impl Iterator<Item = &'a SearchPath> {
+ let kind = self.kind;
+ self.search_paths
+ .iter()
+ .filter(move |sp| sp.kind.matches(kind))
+ .chain(std::iter::once(self.tlib_path))
+ }
+
+ pub fn get_lib_path(&self) -> PathBuf {
+ make_target_lib_path(self.sysroot, self.triple)
+ }
+
+ pub fn get_self_contained_lib_path(&self) -> PathBuf {
+ self.get_lib_path().join("self-contained")
+ }
+
+ pub fn new(
+ sysroot: &'a Path,
+ triple: &'a str,
+ search_paths: &'a [SearchPath],
+ tlib_path: &'a SearchPath,
+ kind: PathKind,
+ ) -> FileSearch<'a> {
+ debug!("using sysroot = {}, triple = {}", sysroot.display(), triple);
+ FileSearch { sysroot, triple, search_paths, tlib_path, kind }
+ }
+
+ /// Returns just the directories within the search paths.
+ pub fn search_path_dirs(&self) -> Vec<PathBuf> {
+ self.search_paths().map(|sp| sp.dir.to_path_buf()).collect()
+ }
+}
+
+pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
+ let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple);
+ PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("lib")])
+}
+
+/// This function checks if sysroot is found using env::args().next(), and if it
+/// is not found, uses env::current_exe() to imply sysroot.
+pub fn get_or_default_sysroot() -> PathBuf {
+ // Follow symlinks. If the resolved path is relative, make it absolute.
+ fn canonicalize(path: PathBuf) -> PathBuf {
+ let path = fs::canonicalize(&path).unwrap_or(path);
+ // See comments on this target function, but the gist is that
+ // gcc chokes on verbatim paths which fs::canonicalize generates
+ // so we try to avoid those kinds of paths.
+ fix_windows_verbatim_for_gcc(&path)
+ }
+
+ // Use env::current_exe() to get the path of the executable following
+ // symlinks/canonicalizing components.
+ fn from_current_exe() -> PathBuf {
+ match env::current_exe() {
+ Ok(exe) => {
+ let mut p = canonicalize(exe);
+ p.pop();
+ p.pop();
+ p
+ }
+ Err(e) => panic!("failed to get current_exe: {e}"),
+ }
+ }
+
+ // Use env::args().next() to get the path of the executable without
+ // following symlinks/canonicalizing any component. This makes the rustc
+ // binary able to locate Rust libraries in systems using content-addressable
+ // storage (CAS).
+ fn from_env_args_next() -> Option<PathBuf> {
+ match env::args_os().next() {
+ Some(first_arg) => {
+ let mut p = PathBuf::from(first_arg);
+
+ // Check if sysroot is found using env::args().next() only if the rustc in argv[0]
+ // is a symlink (see #79253). We might want to change/remove it to conform with
+ // https://www.gnu.org/prep/standards/standards.html#Finding-Program-Files in the
+ // future.
+ if fs::read_link(&p).is_err() {
+ // Path is not a symbolic link or does not exist.
+ return None;
+ }
+
+ // Pop off `bin/rustc`, obtaining the suspected sysroot.
+ p.pop();
+ p.pop();
+ // Look for the target rustlib directory in the suspected sysroot.
+ let mut rustlib_path = rustc_target::target_rustlib_path(&p, "dummy");
+ rustlib_path.pop(); // pop off the dummy target.
+ if rustlib_path.exists() { Some(p) } else { None }
+ }
+ None => None,
+ }
+ }
+
+ // Check if sysroot is found using env::args().next(), and if is not found,
+ // use env::current_exe() to imply sysroot.
+ from_env_args_next().unwrap_or_else(from_current_exe)
+}
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
new file mode 100644
index 000000000..7353c1ca0
--- /dev/null
+++ b/compiler/rustc_session/src/lib.rs
@@ -0,0 +1,40 @@
+#![feature(if_let_guard)]
+#![feature(let_chains)]
+#![feature(let_else)]
+#![feature(min_specialization)]
+#![feature(never_type)]
+#![feature(once_cell)]
+#![feature(option_get_or_insert_default)]
+#![feature(rustc_attrs)]
+#![feature(map_many_mut)]
+#![recursion_limit = "256"]
+#![allow(rustc::potential_query_instability)]
+
+#[macro_use]
+extern crate rustc_macros;
+
+pub mod cgu_reuse_tracker;
+pub mod utils;
+pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass};
+pub use rustc_lint_defs as lint;
+pub mod parse;
+
+mod code_stats;
+#[macro_use]
+pub mod config;
+pub mod cstore;
+pub mod filesearch;
+mod options;
+pub mod search_paths;
+
+mod session;
+pub use session::*;
+
+pub mod output;
+
+pub use getopts;
+
+/// Requirements for a `StableHashingContext` to be used in this crate.
+/// This is a hack to allow using the `HashStable_Generic` derive macro
+/// instead of implementing everything in `rustc_middle`.
+pub trait HashStableContext: rustc_ast::HashStableContext + rustc_hir::HashStableContext {}
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
new file mode 100644
index 000000000..1827f1c20
--- /dev/null
+++ b/compiler/rustc_session/src/options.rs
@@ -0,0 +1,1673 @@
+use crate::config::*;
+
+use crate::early_error;
+use crate::lint;
+use crate::search_paths::SearchPath;
+use crate::utils::NativeLib;
+use rustc_errors::LanguageIdentifier;
+use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy, SanitizerSet};
+use rustc_target::spec::{
+ RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
+};
+
+use rustc_feature::UnstableFeatures;
+use rustc_span::edition::Edition;
+use rustc_span::RealFileName;
+use rustc_span::SourceFileHashAlgorithm;
+
+use std::collections::BTreeMap;
+
+use std::collections::hash_map::DefaultHasher;
+use std::hash::Hasher;
+use std::num::NonZeroUsize;
+use std::path::PathBuf;
+use std::str;
+
+macro_rules! insert {
+ ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr) => {
+ if $sub_hashes
+ .insert(stringify!($opt_name), $opt_expr as &dyn dep_tracking::DepTrackingHash)
+ .is_some()
+ {
+ panic!("duplicate key in CLI DepTrackingHash: {}", stringify!($opt_name))
+ }
+ };
+}
+
+macro_rules! hash_opt {
+ ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, $_for_crate_hash: ident, [UNTRACKED]) => {{}};
+ ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, $_for_crate_hash: ident, [TRACKED]) => {{ insert!($opt_name, $opt_expr, $sub_hashes) }};
+ ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, $for_crate_hash: ident, [TRACKED_NO_CRATE_HASH]) => {{
+ if !$for_crate_hash {
+ insert!($opt_name, $opt_expr, $sub_hashes)
+ }
+ }};
+ ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, $_for_crate_hash: ident, [SUBSTRUCT]) => {{}};
+}
+
+macro_rules! hash_substruct {
+ ($opt_name:ident, $opt_expr:expr, $error_format:expr, $for_crate_hash:expr, $hasher:expr, [UNTRACKED]) => {{}};
+ ($opt_name:ident, $opt_expr:expr, $error_format:expr, $for_crate_hash:expr, $hasher:expr, [TRACKED]) => {{}};
+ ($opt_name:ident, $opt_expr:expr, $error_format:expr, $for_crate_hash:expr, $hasher:expr, [TRACKED_NO_CRATE_HASH]) => {{}};
+ ($opt_name:ident, $opt_expr:expr, $error_format:expr, $for_crate_hash:expr, $hasher:expr, [SUBSTRUCT]) => {
+ use crate::config::dep_tracking::DepTrackingHash;
+ $opt_expr.dep_tracking_hash($for_crate_hash, $error_format).hash(
+ $hasher,
+ $error_format,
+ $for_crate_hash,
+ );
+ };
+}
+
+macro_rules! top_level_options {
+ ( $( #[$top_level_attr:meta] )* pub struct Options { $(
+ $( #[$attr:meta] )*
+ $opt:ident : $t:ty [$dep_tracking_marker:ident],
+ )* } ) => (
+ #[derive(Clone)]
+ $( #[$top_level_attr] )*
+ pub struct Options {
+ $(
+ $( #[$attr] )*
+ pub $opt: $t
+ ),*
+ }
+
+ impl Options {
+ pub fn dep_tracking_hash(&self, for_crate_hash: bool) -> u64 {
+ let mut sub_hashes = BTreeMap::new();
+ $({
+ hash_opt!($opt,
+ &self.$opt,
+ &mut sub_hashes,
+ for_crate_hash,
+ [$dep_tracking_marker]);
+ })*
+ let mut hasher = DefaultHasher::new();
+ dep_tracking::stable_hash(sub_hashes,
+ &mut hasher,
+ self.error_format,
+ for_crate_hash);
+ $({
+ hash_substruct!($opt,
+ &self.$opt,
+ self.error_format,
+ for_crate_hash,
+ &mut hasher,
+ [$dep_tracking_marker]);
+ })*
+ hasher.finish()
+ }
+ }
+ );
+}
+
+top_level_options!(
+ /// The top-level command-line options struct.
+ ///
+ /// For each option, one has to specify how it behaves with regard to the
+ /// dependency tracking system of incremental compilation. This is done via the
+ /// square-bracketed directive after the field type. The options are:
+ ///
+ /// - `[TRACKED]`
+ /// A change in the given field will cause the compiler to completely clear the
+ /// incremental compilation cache before proceeding.
+ ///
+ /// - `[TRACKED_NO_CRATE_HASH]`
+ /// Same as `[TRACKED]`, but will not affect the crate hash. This is useful for options that only
+ /// affect the incremental cache.
+ ///
+ /// - `[UNTRACKED]`
+ /// Incremental compilation is not influenced by this option.
+ ///
+ /// - `[SUBSTRUCT]`
+ /// Second-level sub-structs containing more options.
+ ///
+ /// If you add a new option to this struct or one of the sub-structs like
+ /// `CodegenOptions`, think about how it influences incremental compilation. If in
+ /// doubt, specify `[TRACKED]`, which is always "correct" but might lead to
+ /// unnecessary re-compilation.
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)]
+ pub struct Options {
+ /// The crate config requested for the session, which may be combined
+ /// with additional crate configurations during the compile process.
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::crate_types` instead of this field"))]
+ crate_types: Vec<CrateType> [TRACKED],
+ optimize: OptLevel [TRACKED],
+ /// Include the `debug_assertions` flag in dependency tracking, since it
+ /// can influence whether overflow checks are done or not.
+ debug_assertions: bool [TRACKED],
+ debuginfo: DebugInfo [TRACKED],
+ lint_opts: Vec<(String, lint::Level)> [TRACKED_NO_CRATE_HASH],
+ lint_cap: Option<lint::Level> [TRACKED_NO_CRATE_HASH],
+ describe_lints: bool [UNTRACKED],
+ output_types: OutputTypes [TRACKED],
+ search_paths: Vec<SearchPath> [UNTRACKED],
+ libs: Vec<NativeLib> [TRACKED],
+ maybe_sysroot: Option<PathBuf> [UNTRACKED],
+
+ target_triple: TargetTriple [TRACKED],
+
+ test: bool [TRACKED],
+ error_format: ErrorOutputType [UNTRACKED],
+ diagnostic_width: Option<usize> [UNTRACKED],
+
+ /// If `Some`, enable incremental compilation, using the given
+ /// directory to store intermediate results.
+ incremental: Option<PathBuf> [UNTRACKED],
+ assert_incr_state: Option<IncrementalStateAssertion> [UNTRACKED],
+
+ unstable_opts: UnstableOptions [SUBSTRUCT],
+ prints: Vec<PrintRequest> [UNTRACKED],
+ cg: CodegenOptions [SUBSTRUCT],
+ externs: Externs [UNTRACKED],
+ crate_name: Option<String> [TRACKED],
+ /// Indicates how the compiler should treat unstable features.
+ unstable_features: UnstableFeatures [TRACKED],
+
+ /// Indicates whether this run of the compiler is actually rustdoc. This
+ /// is currently just a hack and will be removed eventually, so please
+ /// try to not rely on this too much.
+ actually_rustdoc: bool [TRACKED],
+
+ /// Control path trimming.
+ trimmed_def_paths: TrimmedDefPaths [TRACKED],
+
+ /// Specifications of codegen units / ThinLTO which are forced as a
+ /// result of parsing command line options. These are not necessarily
+ /// what rustc was invoked with, but massaged a bit to agree with
+ /// commands like `--emit llvm-ir` which they're often incompatible with
+ /// if we otherwise use the defaults of rustc.
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::codegen_units` instead of this field"))]
+ cli_forced_codegen_units: Option<usize> [UNTRACKED],
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))]
+ cli_forced_thinlto_off: bool [UNTRACKED],
+
+ /// Remap source path prefixes in all output (messages, object files, debug, etc.).
+ remap_path_prefix: Vec<(PathBuf, PathBuf)> [TRACKED_NO_CRATE_HASH],
+ /// Base directory containing the `src/` for the Rust standard library, and
+ /// potentially `rustc` as well, if we can can find it. Right now it's always
+ /// `$sysroot/lib/rustlib/src/rust` (i.e. the `rustup` `rust-src` component).
+ ///
+ /// This directory is what the virtual `/rustc/$hash` is translated back to,
+ /// if Rust was built with path remapping to `/rustc/$hash` enabled
+ /// (the `rust.remap-debuginfo` option in `config.toml`).
+ real_rust_source_base_dir: Option<PathBuf> [TRACKED_NO_CRATE_HASH],
+
+ edition: Edition [TRACKED],
+
+ /// `true` if we're emitting JSON blobs about each artifact produced
+ /// by the compiler.
+ json_artifact_notifications: bool [TRACKED],
+
+ /// `true` if we're emitting a JSON blob containing the unused externs
+ json_unused_externs: JsonUnusedExterns [UNTRACKED],
+
+ /// `true` if we're emitting a JSON job containing a future-incompat report for lints
+ json_future_incompat: bool [TRACKED],
+
+ pretty: Option<PpMode> [UNTRACKED],
+
+ /// The (potentially remapped) working directory
+ working_dir: RealFileName [TRACKED],
+ }
+);
+
+/// Defines all `CodegenOptions`/`DebuggingOptions` fields and parsers all at once. The goal of this
+/// macro is to define an interface that can be programmatically used by the option parser
+/// to initialize the struct without hardcoding field names all over the place.
+///
+/// The goal is to invoke this macro once with the correct fields, and then this macro generates all
+/// necessary code. The main gotcha of this macro is the `cgsetters` module which is a bunch of
+/// generated code to parse an option into its respective field in the struct. There are a few
+/// hand-written parsers for parsing specific types of values in this module.
+macro_rules! options {
+ ($struct_name:ident, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr,
+ $($( #[$attr:meta] )* $opt:ident : $t:ty = (
+ $init:expr,
+ $parse:ident,
+ [$dep_tracking_marker:ident],
+ $desc:expr)
+ ),* ,) =>
+(
+ #[derive(Clone)]
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)]
+ pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* }
+
+ impl Default for $struct_name {
+ fn default() -> $struct_name {
+ $struct_name { $($opt: $init),* }
+ }
+ }
+
+ impl $struct_name {
+ pub fn build(
+ matches: &getopts::Matches,
+ error_format: ErrorOutputType,
+ ) -> $struct_name {
+ build_options(matches, $stat, $prefix, $outputname, error_format)
+ }
+
+ fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 {
+ let mut sub_hashes = BTreeMap::new();
+ $({
+ hash_opt!($opt,
+ &self.$opt,
+ &mut sub_hashes,
+ for_crate_hash,
+ [$dep_tracking_marker]);
+ })*
+ let mut hasher = DefaultHasher::new();
+ dep_tracking::stable_hash(sub_hashes,
+ &mut hasher,
+ error_format,
+ for_crate_hash
+ );
+ hasher.finish()
+ }
+ }
+
+ pub const $stat: OptionDescrs<$struct_name> =
+ &[ $( (stringify!($opt), $optmod::$opt, desc::$parse, $desc) ),* ];
+
+ mod $optmod {
+ $(
+ pub(super) fn $opt(cg: &mut super::$struct_name, v: Option<&str>) -> bool {
+ super::parse::$parse(&mut redirect_field!(cg.$opt), v)
+ }
+ )*
+ }
+
+) }
+
+impl Options {
+ // JUSTIFICATION: defn of the suggested wrapper fn
+ #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+ pub fn time_passes(&self) -> bool {
+ self.unstable_opts.time_passes || self.unstable_opts.time
+ }
+}
+
+impl CodegenOptions {
+ // JUSTIFICATION: defn of the suggested wrapper fn
+ #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+ pub fn instrument_coverage(&self) -> InstrumentCoverage {
+ self.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
+ }
+}
+
+// Sometimes different options need to build a common structure.
+// That structure can be kept in one of the options' fields, the others become dummy.
+macro_rules! redirect_field {
+ ($cg:ident.link_arg) => {
+ $cg.link_args
+ };
+ ($cg:ident.pre_link_arg) => {
+ $cg.pre_link_args
+ };
+ ($cg:ident.$field:ident) => {
+ $cg.$field
+ };
+}
+
+type OptionSetter<O> = fn(&mut O, v: Option<&str>) -> bool;
+type OptionDescrs<O> = &'static [(&'static str, OptionSetter<O>, &'static str, &'static str)];
+
+fn build_options<O: Default>(
+ matches: &getopts::Matches,
+ descrs: OptionDescrs<O>,
+ prefix: &str,
+ outputname: &str,
+ error_format: ErrorOutputType,
+) -> O {
+ let mut op = O::default();
+ for option in matches.opt_strs(prefix) {
+ let (key, value) = match option.split_once('=') {
+ None => (option, None),
+ Some((k, v)) => (k.to_string(), Some(v)),
+ };
+
+ let option_to_lookup = key.replace('-', "_");
+ match descrs.iter().find(|(name, ..)| *name == option_to_lookup) {
+ Some((_, setter, type_desc, _)) => {
+ if !setter(&mut op, value) {
+ match value {
+ None => early_error(
+ error_format,
+ &format!(
+ "{0} option `{1}` requires {2} ({3} {1}=<value>)",
+ outputname, key, type_desc, prefix
+ ),
+ ),
+ Some(value) => early_error(
+ error_format,
+ &format!(
+ "incorrect value `{value}` for {outputname} option `{key}` - {type_desc} was expected"
+ ),
+ ),
+ }
+ }
+ }
+ None => early_error(error_format, &format!("unknown {outputname} option: `{key}`")),
+ }
+ }
+ return op;
+}
+
+#[allow(non_upper_case_globals)]
+mod desc {
+ pub const parse_no_flag: &str = "no value";
+ pub const parse_bool: &str = "one of: `y`, `yes`, `on`, `n`, `no`, or `off`";
+ pub const parse_opt_bool: &str = parse_bool;
+ pub const parse_string: &str = "a string";
+ pub const parse_opt_string: &str = parse_string;
+ pub const parse_string_push: &str = parse_string;
+ pub const parse_opt_langid: &str = "a language identifier";
+ pub const parse_opt_pathbuf: &str = "a path";
+ pub const parse_list: &str = "a space-separated list of strings";
+ pub const parse_list_with_polarity: &str =
+ "a comma-separated list of strings, with elements beginning with + or -";
+ pub const parse_opt_comma_list: &str = "a comma-separated list of strings";
+ pub const parse_number: &str = "a number";
+ pub const parse_opt_number: &str = parse_number;
+ pub const parse_threads: &str = parse_number;
+ pub const parse_passes: &str = "a space-separated list of passes, or `all`";
+ pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
+ pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
+ pub const parse_oom_strategy: &str = "either `panic` or `abort`";
+ pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
+ pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
+ pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
+ pub const parse_cfguard: &str =
+ "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
+ pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)";
+ pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
+ pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of();
+ pub const parse_optimization_fuel: &str = "crate=integer";
+ pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`";
+ pub const parse_instrument_coverage: &str =
+ "`all` (default), `except-unused-generics`, `except-unused-functions`, or `off`";
+ pub const parse_unpretty: &str = "`string` or `string=string`";
+ pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0";
+ pub const parse_lto: &str =
+ "either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted";
+ pub const parse_linker_plugin_lto: &str =
+ "either a boolean (`yes`, `no`, `on`, `off`, etc), or the path to the linker plugin";
+ pub const parse_location_detail: &str = "either `none`, or a comma separated list of location details to track: `file`, `line`, or `column`";
+ pub const parse_switch_with_opt_path: &str =
+ "an optional path to the profiling data output directory";
+ pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`";
+ pub const parse_symbol_mangling_version: &str = "either `legacy` or `v0` (RFC 2603)";
+ pub const parse_src_file_hash: &str = "either `md5` or `sha1`";
+ pub const parse_relocation_model: &str =
+ "one of supported relocation models (`rustc --print relocation-models`)";
+ pub const parse_code_model: &str = "one of supported code models (`rustc --print code-models`)";
+ pub const parse_tls_model: &str = "one of supported TLS models (`rustc --print tls-models`)";
+ pub const parse_target_feature: &str = parse_string;
+ pub const parse_wasi_exec_model: &str = "either `command` or `reactor`";
+ pub const parse_split_debuginfo: &str =
+ "one of supported split-debuginfo modes (`off`, `packed`, or `unpacked`)";
+ pub const parse_split_dwarf_kind: &str =
+ "one of supported split dwarf modes (`split` or `single`)";
+ pub const parse_gcc_ld: &str = "one of: no value, `lld`";
+ pub const parse_stack_protector: &str =
+ "one of (`none` (default), `basic`, `strong`, or `all`)";
+ pub const parse_branch_protection: &str =
+ "a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`";
+ pub const parse_proc_macro_execution_strategy: &str =
+ "one of supported execution strategies (`same-thread`, or `cross-thread`)";
+}
+
+mod parse {
+ pub(crate) use super::*;
+ use std::str::FromStr;
+
+ /// This is for boolean options that don't take a value and start with
+ /// `no-`. This style of option is deprecated.
+ pub(crate) fn parse_no_flag(slot: &mut bool, v: Option<&str>) -> bool {
+ match v {
+ None => {
+ *slot = true;
+ true
+ }
+ Some(_) => false,
+ }
+ }
+
+ /// Use this for any boolean option that has a static default.
+ pub(crate) fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool {
+ match v {
+ Some("y") | Some("yes") | Some("on") | None => {
+ *slot = true;
+ true
+ }
+ Some("n") | Some("no") | Some("off") => {
+ *slot = false;
+ true
+ }
+ _ => false,
+ }
+ }
+
+ /// Use this for any boolean option that lacks a static default. (The
+ /// actions taken when such an option is not specified will depend on
+ /// other factors, such as other options, or target options.)
+ pub(crate) fn parse_opt_bool(slot: &mut Option<bool>, v: Option<&str>) -> bool {
+ match v {
+ Some("y") | Some("yes") | Some("on") | None => {
+ *slot = Some(true);
+ true
+ }
+ Some("n") | Some("no") | Some("off") => {
+ *slot = Some(false);
+ true
+ }
+ _ => false,
+ }
+ }
+
+ /// Use this for any string option that has a static default.
+ pub(crate) fn parse_string(slot: &mut String, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => {
+ *slot = s.to_string();
+ true
+ }
+ None => false,
+ }
+ }
+
+ /// Use this for any string option that lacks a static default.
+ pub(crate) fn parse_opt_string(slot: &mut Option<String>, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => {
+ *slot = Some(s.to_string());
+ true
+ }
+ None => false,
+ }
+ }
+
+ /// Parse an optional language identifier, e.g. `en-US` or `zh-CN`.
+ pub(crate) fn parse_opt_langid(slot: &mut Option<LanguageIdentifier>, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => {
+ *slot = rustc_errors::LanguageIdentifier::from_str(s).ok();
+ true
+ }
+ None => false,
+ }
+ }
+
+ pub(crate) fn parse_opt_pathbuf(slot: &mut Option<PathBuf>, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => {
+ *slot = Some(PathBuf::from(s));
+ true
+ }
+ None => false,
+ }
+ }
+
+ pub(crate) fn parse_string_push(slot: &mut Vec<String>, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => {
+ slot.push(s.to_string());
+ true
+ }
+ None => false,
+ }
+ }
+
+ pub(crate) fn parse_list(slot: &mut Vec<String>, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => {
+ slot.extend(s.split_whitespace().map(|s| s.to_string()));
+ true
+ }
+ None => false,
+ }
+ }
+
+ pub(crate) fn parse_list_with_polarity(
+ slot: &mut Vec<(String, bool)>,
+ v: Option<&str>,
+ ) -> bool {
+ match v {
+ Some(s) => {
+ for s in s.split(',') {
+ let Some(pass_name) = s.strip_prefix(&['+', '-'][..]) else { return false };
+ slot.push((pass_name.to_string(), &s[..1] == "+"));
+ }
+ true
+ }
+ None => false,
+ }
+ }
+
+ pub(crate) fn parse_location_detail(ld: &mut LocationDetail, v: Option<&str>) -> bool {
+ if let Some(v) = v {
+ ld.line = false;
+ ld.file = false;
+ ld.column = false;
+ if v == "none" {
+ return true;
+ }
+ for s in v.split(',') {
+ match s {
+ "file" => ld.file = true,
+ "line" => ld.line = true,
+ "column" => ld.column = true,
+ _ => return false,
+ }
+ }
+ true
+ } else {
+ false
+ }
+ }
+
+ pub(crate) fn parse_opt_comma_list(slot: &mut Option<Vec<String>>, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => {
+ let mut v: Vec<_> = s.split(',').map(|s| s.to_string()).collect();
+ v.sort_unstable();
+ *slot = Some(v);
+ true
+ }
+ None => false,
+ }
+ }
+
+ pub(crate) fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool {
+ match v.and_then(|s| s.parse().ok()) {
+ Some(0) => {
+ *slot = ::num_cpus::get();
+ true
+ }
+ Some(i) => {
+ *slot = i;
+ true
+ }
+ None => false,
+ }
+ }
+
+ /// Use this for any numeric option that has a static default.
+ pub(crate) fn parse_number<T: Copy + FromStr>(slot: &mut T, v: Option<&str>) -> bool {
+ match v.and_then(|s| s.parse().ok()) {
+ Some(i) => {
+ *slot = i;
+ true
+ }
+ None => false,
+ }
+ }
+
+ /// Use this for any numeric option that lacks a static default.
+ pub(crate) fn parse_opt_number<T: Copy + FromStr>(
+ slot: &mut Option<T>,
+ v: Option<&str>,
+ ) -> bool {
+ match v {
+ Some(s) => {
+ *slot = s.parse().ok();
+ slot.is_some()
+ }
+ None => false,
+ }
+ }
+
+ pub(crate) fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool {
+ match v {
+ Some("all") => {
+ *slot = Passes::All;
+ true
+ }
+ v => {
+ let mut passes = vec![];
+ if parse_list(&mut passes, v) {
+ slot.extend(passes);
+ true
+ } else {
+ false
+ }
+ }
+ }
+ }
+
+ pub(crate) fn parse_opt_panic_strategy(
+ slot: &mut Option<PanicStrategy>,
+ v: Option<&str>,
+ ) -> bool {
+ match v {
+ Some("unwind") => *slot = Some(PanicStrategy::Unwind),
+ Some("abort") => *slot = Some(PanicStrategy::Abort),
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_panic_strategy(slot: &mut PanicStrategy, v: Option<&str>) -> bool {
+ match v {
+ Some("unwind") => *slot = PanicStrategy::Unwind,
+ Some("abort") => *slot = PanicStrategy::Abort,
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool {
+ match v {
+ Some("panic") => *slot = OomStrategy::Panic,
+ Some("abort") => *slot = OomStrategy::Abort,
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_relro_level(slot: &mut Option<RelroLevel>, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => match s.parse::<RelroLevel>() {
+ Ok(level) => *slot = Some(level),
+ _ => return false,
+ },
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_sanitizers(slot: &mut SanitizerSet, v: Option<&str>) -> bool {
+ if let Some(v) = v {
+ for s in v.split(',') {
+ *slot |= match s {
+ "address" => SanitizerSet::ADDRESS,
+ "cfi" => SanitizerSet::CFI,
+ "leak" => SanitizerSet::LEAK,
+ "memory" => SanitizerSet::MEMORY,
+ "memtag" => SanitizerSet::MEMTAG,
+ "shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
+ "thread" => SanitizerSet::THREAD,
+ "hwaddress" => SanitizerSet::HWADDRESS,
+ _ => return false,
+ }
+ }
+ true
+ } else {
+ false
+ }
+ }
+
+ pub(crate) fn parse_sanitizer_memory_track_origins(slot: &mut usize, v: Option<&str>) -> bool {
+ match v {
+ Some("2") | None => {
+ *slot = 2;
+ true
+ }
+ Some("1") => {
+ *slot = 1;
+ true
+ }
+ Some("0") => {
+ *slot = 0;
+ true
+ }
+ Some(_) => false,
+ }
+ }
+
+ pub(crate) fn parse_strip(slot: &mut Strip, v: Option<&str>) -> bool {
+ match v {
+ Some("none") => *slot = Strip::None,
+ Some("debuginfo") => *slot = Strip::Debuginfo,
+ Some("symbols") => *slot = Strip::Symbols,
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool {
+ if v.is_some() {
+ let mut bool_arg = None;
+ if parse_opt_bool(&mut bool_arg, v) {
+ *slot = if bool_arg.unwrap() { CFGuard::Checks } else { CFGuard::Disabled };
+ return true;
+ }
+ }
+
+ *slot = match v {
+ None => CFGuard::Checks,
+ Some("checks") => CFGuard::Checks,
+ Some("nochecks") => CFGuard::NoChecks,
+ Some(_) => return false,
+ };
+ true
+ }
+
+ pub(crate) fn parse_cfprotection(slot: &mut CFProtection, v: Option<&str>) -> bool {
+ if v.is_some() {
+ let mut bool_arg = None;
+ if parse_opt_bool(&mut bool_arg, v) {
+ *slot = if bool_arg.unwrap() { CFProtection::Full } else { CFProtection::None };
+ return true;
+ }
+ }
+
+ *slot = match v {
+ None | Some("none") => CFProtection::None,
+ Some("branch") => CFProtection::Branch,
+ Some("return") => CFProtection::Return,
+ Some("full") => CFProtection::Full,
+ Some(_) => return false,
+ };
+ true
+ }
+
+ pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
+ match v.and_then(LinkerFlavor::from_str) {
+ Some(lf) => *slot = Some(lf),
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_optimization_fuel(
+ slot: &mut Option<(String, u64)>,
+ v: Option<&str>,
+ ) -> bool {
+ match v {
+ None => false,
+ Some(s) => {
+ let parts = s.split('=').collect::<Vec<_>>();
+ if parts.len() != 2 {
+ return false;
+ }
+ let crate_name = parts[0].to_string();
+ let fuel = parts[1].parse::<u64>();
+ if fuel.is_err() {
+ return false;
+ }
+ *slot = Some((crate_name, fuel.unwrap()));
+ true
+ }
+ }
+ }
+
+ pub(crate) fn parse_unpretty(slot: &mut Option<String>, v: Option<&str>) -> bool {
+ match v {
+ None => false,
+ Some(s) if s.split('=').count() <= 2 => {
+ *slot = Some(s.to_string());
+ true
+ }
+ _ => false,
+ }
+ }
+
+ pub(crate) fn parse_mir_spanview(slot: &mut Option<MirSpanview>, v: Option<&str>) -> bool {
+ if v.is_some() {
+ let mut bool_arg = None;
+ if parse_opt_bool(&mut bool_arg, v) {
+ *slot = if bool_arg.unwrap() { Some(MirSpanview::Statement) } else { None };
+ return true;
+ }
+ }
+
+ let Some(v) = v else {
+ *slot = Some(MirSpanview::Statement);
+ return true;
+ };
+
+ *slot = Some(match v.trim_end_matches('s') {
+ "statement" | "stmt" => MirSpanview::Statement,
+ "terminator" | "term" => MirSpanview::Terminator,
+ "block" | "basicblock" => MirSpanview::Block,
+ _ => return false,
+ });
+ true
+ }
+
+ pub(crate) fn parse_instrument_coverage(
+ slot: &mut Option<InstrumentCoverage>,
+ v: Option<&str>,
+ ) -> bool {
+ if v.is_some() {
+ let mut bool_arg = None;
+ if parse_opt_bool(&mut bool_arg, v) {
+ *slot = if bool_arg.unwrap() { Some(InstrumentCoverage::All) } else { None };
+ return true;
+ }
+ }
+
+ let Some(v) = v else {
+ *slot = Some(InstrumentCoverage::All);
+ return true;
+ };
+
+ *slot = Some(match v {
+ "all" => InstrumentCoverage::All,
+ "except-unused-generics" | "except_unused_generics" => {
+ InstrumentCoverage::ExceptUnusedGenerics
+ }
+ "except-unused-functions" | "except_unused_functions" => {
+ InstrumentCoverage::ExceptUnusedFunctions
+ }
+ "off" | "no" | "n" | "false" | "0" => InstrumentCoverage::Off,
+ _ => return false,
+ });
+ true
+ }
+
+ pub(crate) fn parse_treat_err_as_bug(slot: &mut Option<NonZeroUsize>, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => {
+ *slot = s.parse().ok();
+ slot.is_some()
+ }
+ None => {
+ *slot = NonZeroUsize::new(1);
+ true
+ }
+ }
+ }
+
+ pub(crate) fn parse_lto(slot: &mut LtoCli, v: Option<&str>) -> bool {
+ if v.is_some() {
+ let mut bool_arg = None;
+ if parse_opt_bool(&mut bool_arg, v) {
+ *slot = if bool_arg.unwrap() { LtoCli::Yes } else { LtoCli::No };
+ return true;
+ }
+ }
+
+ *slot = match v {
+ None => LtoCli::NoParam,
+ Some("thin") => LtoCli::Thin,
+ Some("fat") => LtoCli::Fat,
+ Some(_) => return false,
+ };
+ true
+ }
+
+ pub(crate) fn parse_linker_plugin_lto(slot: &mut LinkerPluginLto, v: Option<&str>) -> bool {
+ if v.is_some() {
+ let mut bool_arg = None;
+ if parse_opt_bool(&mut bool_arg, v) {
+ *slot = if bool_arg.unwrap() {
+ LinkerPluginLto::LinkerPluginAuto
+ } else {
+ LinkerPluginLto::Disabled
+ };
+ return true;
+ }
+ }
+
+ *slot = match v {
+ None => LinkerPluginLto::LinkerPluginAuto,
+ Some(path) => LinkerPluginLto::LinkerPlugin(PathBuf::from(path)),
+ };
+ true
+ }
+
+ pub(crate) fn parse_switch_with_opt_path(
+ slot: &mut SwitchWithOptPath,
+ v: Option<&str>,
+ ) -> bool {
+ *slot = match v {
+ None => SwitchWithOptPath::Enabled(None),
+ Some(path) => SwitchWithOptPath::Enabled(Some(PathBuf::from(path))),
+ };
+ true
+ }
+
+ pub(crate) fn parse_merge_functions(
+ slot: &mut Option<MergeFunctions>,
+ v: Option<&str>,
+ ) -> bool {
+ match v.and_then(|s| MergeFunctions::from_str(s).ok()) {
+ Some(mergefunc) => *slot = Some(mergefunc),
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_relocation_model(slot: &mut Option<RelocModel>, v: Option<&str>) -> bool {
+ match v.and_then(|s| RelocModel::from_str(s).ok()) {
+ Some(relocation_model) => *slot = Some(relocation_model),
+ None if v == Some("default") => *slot = None,
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_code_model(slot: &mut Option<CodeModel>, v: Option<&str>) -> bool {
+ match v.and_then(|s| CodeModel::from_str(s).ok()) {
+ Some(code_model) => *slot = Some(code_model),
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_tls_model(slot: &mut Option<TlsModel>, v: Option<&str>) -> bool {
+ match v.and_then(|s| TlsModel::from_str(s).ok()) {
+ Some(tls_model) => *slot = Some(tls_model),
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_symbol_mangling_version(
+ slot: &mut Option<SymbolManglingVersion>,
+ v: Option<&str>,
+ ) -> bool {
+ *slot = match v {
+ Some("legacy") => Some(SymbolManglingVersion::Legacy),
+ Some("v0") => Some(SymbolManglingVersion::V0),
+ _ => return false,
+ };
+ true
+ }
+
+ pub(crate) fn parse_src_file_hash(
+ slot: &mut Option<SourceFileHashAlgorithm>,
+ v: Option<&str>,
+ ) -> bool {
+ match v.and_then(|s| SourceFileHashAlgorithm::from_str(s).ok()) {
+ Some(hash_kind) => *slot = Some(hash_kind),
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => {
+ if !slot.is_empty() {
+ slot.push(',');
+ }
+ slot.push_str(s);
+ true
+ }
+ None => false,
+ }
+ }
+
+ pub(crate) fn parse_wasi_exec_model(slot: &mut Option<WasiExecModel>, v: Option<&str>) -> bool {
+ match v {
+ Some("command") => *slot = Some(WasiExecModel::Command),
+ Some("reactor") => *slot = Some(WasiExecModel::Reactor),
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_split_debuginfo(
+ slot: &mut Option<SplitDebuginfo>,
+ v: Option<&str>,
+ ) -> bool {
+ match v.and_then(|s| SplitDebuginfo::from_str(s).ok()) {
+ Some(e) => *slot = Some(e),
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_split_dwarf_kind(slot: &mut SplitDwarfKind, v: Option<&str>) -> bool {
+ match v.and_then(|s| SplitDwarfKind::from_str(s).ok()) {
+ Some(e) => *slot = e,
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_gcc_ld(slot: &mut Option<LdImpl>, v: Option<&str>) -> bool {
+ match v {
+ None => *slot = None,
+ Some("lld") => *slot = Some(LdImpl::Lld),
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_stack_protector(slot: &mut StackProtector, v: Option<&str>) -> bool {
+ match v.and_then(|s| StackProtector::from_str(s).ok()) {
+ Some(ssp) => *slot = ssp,
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_branch_protection(
+ slot: &mut Option<BranchProtection>,
+ v: Option<&str>,
+ ) -> bool {
+ match v {
+ Some(s) => {
+ let slot = slot.get_or_insert_default();
+ for opt in s.split(',') {
+ match opt {
+ "bti" => slot.bti = true,
+ "pac-ret" if slot.pac_ret.is_none() => {
+ slot.pac_ret = Some(PacRet { leaf: false, key: PAuthKey::A })
+ }
+ "leaf" => match slot.pac_ret.as_mut() {
+ Some(pac) => pac.leaf = true,
+ _ => return false,
+ },
+ "b-key" => match slot.pac_ret.as_mut() {
+ Some(pac) => pac.key = PAuthKey::B,
+ _ => return false,
+ },
+ _ => return false,
+ };
+ }
+ }
+ _ => return false,
+ }
+ true
+ }
+
+ pub(crate) fn parse_proc_macro_execution_strategy(
+ slot: &mut ProcMacroExecutionStrategy,
+ v: Option<&str>,
+ ) -> bool {
+ *slot = match v {
+ Some("same-thread") => ProcMacroExecutionStrategy::SameThread,
+ Some("cross-thread") => ProcMacroExecutionStrategy::CrossThread,
+ _ => return false,
+ };
+ true
+ }
+}
+
+options! {
+ CodegenOptions, CG_OPTIONS, cgopts, "C", "codegen",
+
+ // This list is in alphabetical order.
+ //
+ // If you add a new option, please update:
+ // - compiler/rustc_interface/src/tests.rs
+ // - src/doc/rustc/src/codegen-options/index.md
+
+ ar: String = (String::new(), parse_string, [UNTRACKED],
+ "this option is deprecated and does nothing"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field"))]
+ code_model: Option<CodeModel> = (None, parse_code_model, [TRACKED],
+ "choose the code model to use (`rustc --print code-models` for details)"),
+ codegen_units: Option<usize> = (None, parse_opt_number, [UNTRACKED],
+ "divide crate into N units to optimize in parallel"),
+ control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [TRACKED],
+ "use Windows Control Flow Guard (default: no)"),
+ debug_assertions: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "explicitly enable the `cfg(debug_assertions)` directive"),
+ debuginfo: usize = (0, parse_number, [TRACKED],
+ "debug info emission level (0 = no debug info, 1 = line tables only, \
+ 2 = full debug info with variable and type information; default: 0)"),
+ default_linker_libraries: bool = (false, parse_bool, [UNTRACKED],
+ "allow the linker to link its default libraries (default: no)"),
+ embed_bitcode: bool = (true, parse_bool, [TRACKED],
+ "emit bitcode in rlibs (default: yes)"),
+ extra_filename: String = (String::new(), parse_string, [UNTRACKED],
+ "extra data to put in each output filename"),
+ force_frame_pointers: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "force use of the frame pointers"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::must_emit_unwind_tables` instead of this field"))]
+ force_unwind_tables: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "force use of unwind tables"),
+ incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
+ "enable incremental compilation"),
+ inline_threshold: Option<u32> = (None, parse_opt_number, [TRACKED],
+ "set the threshold for inlining a function"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))]
+ instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED],
+ "instrument the generated code to support LLVM source-based code coverage \
+ reports (note, the compiler build config must include `profiler = true`); \
+ implies `-C symbol-mangling-version=v0`. Optional values are:
+ `=all` (implicit value)
+ `=except-unused-generics`
+ `=except-unused-functions`
+ `=off` (default)"),
+ link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED],
+ "a single extra argument to append to the linker invocation (can be used several times)"),
+ link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED],
+ "extra arguments to append to the linker invocation (space separated)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::link_dead_code` instead of this field"))]
+ link_dead_code: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "keep dead code at link time (useful for code coverage) (default: no)"),
+ link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
+ "control whether to link Rust provided C objects/libraries or rely
+ on C toolchain installed in the system"),
+ linker: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
+ "system linker to link outputs with"),
+ linker_flavor: Option<LinkerFlavor> = (None, parse_linker_flavor, [UNTRACKED],
+ "linker flavor"),
+ linker_plugin_lto: LinkerPluginLto = (LinkerPluginLto::Disabled,
+ parse_linker_plugin_lto, [TRACKED],
+ "generate build artifacts that are compatible with linker-based LTO"),
+ llvm_args: Vec<String> = (Vec::new(), parse_list, [TRACKED],
+ "a list of arguments to pass to LLVM (space separated)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))]
+ lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED],
+ "perform LLVM link-time optimizations"),
+ metadata: Vec<String> = (Vec::new(), parse_list, [TRACKED],
+ "metadata to mangle symbol names with"),
+ no_prepopulate_passes: bool = (false, parse_no_flag, [TRACKED],
+ "give an empty list of passes to the pass manager"),
+ no_redzone: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "disable the use of the redzone"),
+ no_stack_check: bool = (false, parse_no_flag, [UNTRACKED],
+ "this option is deprecated and does nothing"),
+ no_vectorize_loops: bool = (false, parse_no_flag, [TRACKED],
+ "disable loop vectorization optimization passes"),
+ no_vectorize_slp: bool = (false, parse_no_flag, [TRACKED],
+ "disable LLVM's SLP vectorization pass"),
+ opt_level: String = ("0".to_string(), parse_string, [TRACKED],
+ "optimization level (0-3, s, or z; default: 0)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::overflow_checks` instead of this field"))]
+ overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "use overflow checks for integer arithmetic"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::panic_strategy` instead of this field"))]
+ panic: Option<PanicStrategy> = (None, parse_opt_panic_strategy, [TRACKED],
+ "panic strategy to compile crate with"),
+ passes: Vec<String> = (Vec::new(), parse_list, [TRACKED],
+ "a list of extra LLVM passes to run (space separated)"),
+ prefer_dynamic: bool = (false, parse_bool, [TRACKED],
+ "prefer dynamic linking to static linking (default: no)"),
+ profile_generate: SwitchWithOptPath = (SwitchWithOptPath::Disabled,
+ parse_switch_with_opt_path, [TRACKED],
+ "compile the program with profiling instrumentation"),
+ profile_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
+ "use the given `.profdata` file for profile-guided optimization"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::relocation_model` instead of this field"))]
+ relocation_model: Option<RelocModel> = (None, parse_relocation_model, [TRACKED],
+ "control generation of position-independent code (PIC) \
+ (`rustc --print relocation-models` for details)"),
+ remark: Passes = (Passes::Some(Vec::new()), parse_passes, [UNTRACKED],
+ "print remarks for these optimization passes (space separated, or \"all\")"),
+ rpath: bool = (false, parse_bool, [UNTRACKED],
+ "set rpath values in libs/exes (default: no)"),
+ save_temps: bool = (false, parse_bool, [UNTRACKED],
+ "save all temporary output files during compilation (default: no)"),
+ soft_float: bool = (false, parse_bool, [TRACKED],
+ "use soft float ABI (*eabihf targets only) (default: no)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field"))]
+ split_debuginfo: Option<SplitDebuginfo> = (None, parse_split_debuginfo, [TRACKED],
+ "how to handle split-debuginfo, a platform-specific option"),
+ strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
+ "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
+ symbol_mangling_version: Option<SymbolManglingVersion> = (None,
+ parse_symbol_mangling_version, [TRACKED],
+ "which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
+ target_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
+ "select target processor (`rustc --print target-cpus` for details)"),
+ target_feature: String = (String::new(), parse_target_feature, [TRACKED],
+ "target specific attributes. (`rustc --print target-features` for details). \
+ This feature is unsafe."),
+
+ // This list is in alphabetical order.
+ //
+ // If you add a new option, please update:
+ // - compiler/rustc_interface/src/tests.rs
+ // - src/doc/rustc/src/codegen-options/index.md
+}
+
+options! {
+ UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable",
+
+ // This list is in alphabetical order.
+ //
+ // If you add a new option, please update:
+ // - compiler/rustc_interface/src/tests.rs
+ // - src/doc/unstable-book/src/compiler-flags
+
+ allow_features: Option<Vec<String>> = (None, parse_opt_comma_list, [TRACKED],
+ "only allow the listed language features to be enabled in code (space separated)"),
+ always_encode_mir: bool = (false, parse_bool, [TRACKED],
+ "encode MIR of all functions into the crate metadata (default: no)"),
+ assume_incomplete_release: bool = (false, parse_bool, [TRACKED],
+ "make cfg(version) treat the current version as incomplete (default: no)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field"))]
+ asm_comments: bool = (false, parse_bool, [TRACKED],
+ "generate comments into the assembly (may change behavior) (default: no)"),
+ assert_incr_state: Option<String> = (None, parse_opt_string, [UNTRACKED],
+ "assert that the incremental cache is in given state: \
+ either `loaded` or `not-loaded`."),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field"))]
+ binary_dep_depinfo: bool = (false, parse_bool, [TRACKED],
+ "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \
+ (default: no)"),
+ box_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "emit noalias metadata for box (default: yes)"),
+ branch_protection: Option<BranchProtection> = (None, parse_branch_protection, [TRACKED],
+ "set options for branch target identification and pointer authentication on AArch64"),
+ cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED],
+ "instrument control-flow architecture protection"),
+ cgu_partitioning_strategy: Option<String> = (None, parse_opt_string, [TRACKED],
+ "the codegen unit partitioning strategy to use"),
+ chalk: bool = (false, parse_bool, [TRACKED],
+ "enable the experimental Chalk-based trait solving engine"),
+ codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED],
+ "the backend to use"),
+ combine_cgu: bool = (false, parse_bool, [TRACKED],
+ "combine CGUs into a single one"),
+ crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
+ "inject the given attribute in the crate"),
+ debug_info_for_profiling: bool = (false, parse_bool, [TRACKED],
+ "emit discriminators and other data necessary for AutoFDO"),
+ debug_macros: bool = (false, parse_bool, [TRACKED],
+ "emit line numbers debug info inside macros (default: no)"),
+ deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED],
+ "deduplicate identical diagnostics (default: yes)"),
+ dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED],
+ "in dep-info output, omit targets for tracking dependencies of the dep-info files \
+ themselves (default: no)"),
+ dep_tasks: bool = (false, parse_bool, [UNTRACKED],
+ "print tasks that execute and the color their dep node gets (requires debug build) \
+ (default: no)"),
+ dlltool: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
+ "import library generation tool (windows-gnu only)"),
+ dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED],
+ "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \
+ (default: no)"),
+ drop_tracking: bool = (false, parse_bool, [TRACKED],
+ "enables drop tracking in generators (default: no)"),
+ dual_proc_macros: bool = (false, parse_bool, [TRACKED],
+ "load proc macros for both target and host, but only link to the target (default: no)"),
+ dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
+ "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \
+ (default: no)"),
+ dump_drop_tracking_cfg: Option<String> = (None, parse_opt_string, [UNTRACKED],
+ "dump drop-tracking control-flow graph as a `.dot` file (default: no)"),
+ dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED],
+ "dump MIR state to file.
+ `val` is used to select which passes and functions to dump. For example:
+ `all` matches all passes and functions,
+ `foo` matches all passes for functions whose name contains 'foo',
+ `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo',
+ `foo | bar` all passes for function names containing 'foo' or 'bar'."),
+ dump_mir_dataflow: bool = (false, parse_bool, [UNTRACKED],
+ "in addition to `.mir` files, create graphviz `.dot` files with dataflow results \
+ (default: no)"),
+ dump_mir_dir: String = ("mir_dump".to_string(), parse_string, [UNTRACKED],
+ "the directory the MIR is dumped into (default: `mir_dump`)"),
+ dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED],
+ "exclude the pass number when dumping MIR (used in tests) (default: no)"),
+ dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED],
+ "in addition to `.mir` files, create graphviz `.dot` files (and with \
+ `-Z instrument-coverage`, also create a `.dot` file for the MIR-derived \
+ coverage graph) (default: no)"),
+ dump_mir_spanview: Option<MirSpanview> = (None, parse_mir_spanview, [UNTRACKED],
+ "in addition to `.mir` files, create `.html` files to view spans for \
+ all `statement`s (including terminators), only `terminator` spans, or \
+ computed `block` spans (one span encompassing a block's terminator and \
+ all statements). If `-Z instrument-coverage` is also enabled, create \
+ an additional `.html` file showing the computed coverage spans."),
+ dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
+ "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
+ emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
+ "emit a section containing stack size metadata (default: no)"),
+ emit_thin_lto: bool = (true, parse_bool, [TRACKED],
+ "emit the bc module with thin LTO info (default: yes)"),
+ export_executable_symbols: bool = (false, parse_bool, [TRACKED],
+ "export symbols from executables, as if they were dynamic libraries"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::fewer_names` instead of this field"))]
+ fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \
+ (default: no)"),
+ force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED],
+ "force all crates to be `rustc_private` unstable (default: no)"),
+ fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],
+ "set the optimization fuel quota for a crate"),
+ function_sections: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "whether each function should go in its own section"),
+ future_incompat_test: bool = (false, parse_bool, [UNTRACKED],
+ "forces all lints to be future incompatible, used for internal testing (default: no)"),
+ gcc_ld: Option<LdImpl> = (None, parse_gcc_ld, [TRACKED], "implementation of ld used by cc"),
+ graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED],
+ "use dark-themed colors in graphviz output (default: no)"),
+ graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED],
+ "use the given `fontname` in graphviz output; can be overridden by setting \
+ environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"),
+ hir_stats: bool = (false, parse_bool, [UNTRACKED],
+ "print some statistics about AST and HIR (default: no)"),
+ human_readable_cgu_names: bool = (false, parse_bool, [TRACKED],
+ "generate human-readable, predictable names for codegen units (default: no)"),
+ identify_regions: bool = (false, parse_bool, [UNTRACKED],
+ "display unnamed regions as `'<id>`, using a non-ident unique id (default: no)"),
+ incremental_ignore_spans: bool = (false, parse_bool, [UNTRACKED],
+ "ignore spans during ICH computation -- used for testing (default: no)"),
+ incremental_info: bool = (false, parse_bool, [UNTRACKED],
+ "print high-level information about incremental reuse (or the lack thereof) \
+ (default: no)"),
+ incremental_relative_spans: bool = (false, parse_bool, [TRACKED],
+ "hash spans relative to their parent item for incr. comp. (default: no)"),
+ incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED],
+ "verify incr. comp. hashes of green query instances (default: no)"),
+ inline_mir: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "enable MIR inlining (default: no)"),
+ inline_mir_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
+ "a default MIR inlining threshold (default: 50)"),
+ inline_mir_hint_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
+ "inlining threshold for functions with inline hint (default: 100)"),
+ inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "control whether `#[inline]` functions are in all CGUs"),
+ input_stats: bool = (false, parse_bool, [UNTRACKED],
+ "gather statistics about the input (default: no)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))]
+ instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED],
+ "instrument the generated code to support LLVM source-based code coverage \
+ reports (note, the compiler build config must include `profiler = true`); \
+ implies `-C symbol-mangling-version=v0`. Optional values are:
+ `=all` (implicit value)
+ `=except-unused-generics`
+ `=except-unused-functions`
+ `=off` (default)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_mcount` instead of this field"))]
+ instrument_mcount: bool = (false, parse_bool, [TRACKED],
+ "insert function instrument code for mcount-based tracing (default: no)"),
+ keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],
+ "keep hygiene data after analysis (default: no)"),
+ link_native_libraries: bool = (true, parse_bool, [UNTRACKED],
+ "link native libraries in the linker invocation (default: yes)"),
+ link_only: bool = (false, parse_bool, [TRACKED],
+ "link the `.rlink` file generated by `-Z no-link` (default: no)"),
+ llvm_plugins: Vec<String> = (Vec::new(), parse_list, [TRACKED],
+ "a list LLVM plugins to enable (space separated)"),
+ llvm_time_trace: bool = (false, parse_bool, [UNTRACKED],
+ "generate JSON tracing data file from LLVM data (default: no)"),
+ location_detail: LocationDetail = (LocationDetail::all(), parse_location_detail, [TRACKED],
+ "what location details should be tracked when using caller_location, either \
+ `none`, or a comma separated list of location details, for which \
+ valid options are `file`, `line`, and `column` (default: `file,line,column`)"),
+ ls: bool = (false, parse_bool, [UNTRACKED],
+ "list the symbols defined by a library crate (default: no)"),
+ macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
+ "show macro backtraces (default: no)"),
+ merge_functions: Option<MergeFunctions> = (None, parse_merge_functions, [TRACKED],
+ "control the operation of the MergeFunctions LLVM pass, taking \
+ the same values as the target option of the same name"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::meta_stats` instead of this field"))]
+ meta_stats: bool = (false, parse_bool, [UNTRACKED],
+ "gather metadata statistics (default: no)"),
+ mir_emit_retag: bool = (false, parse_bool, [TRACKED],
+ "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
+ (default: no)"),
+ mir_enable_passes: Vec<(String, bool)> = (Vec::new(), parse_list_with_polarity, [TRACKED],
+ "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \
+ enabled, overriding all other checks. Passes that are not specified are enabled or \
+ disabled by other flags as usual."),
+ mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED],
+ "use line numbers relative to the function in mir pretty printing"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field"))]
+ mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED],
+ "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
+ move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED],
+ "the size at which the `large_assignments` lint starts to be emitted"),
+ mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "emit noalias metadata for mutable references (default: yes)"),
+ new_llvm_pass_manager: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "use new LLVM pass manager (default: no)"),
+ nll_facts: bool = (false, parse_bool, [UNTRACKED],
+ "dump facts from NLL analysis into side files (default: no)"),
+ nll_facts_dir: String = ("nll-facts".to_string(), parse_string, [UNTRACKED],
+ "the directory the NLL facts are dumped into (default: `nll-facts`)"),
+ no_analysis: bool = (false, parse_no_flag, [UNTRACKED],
+ "parse and expand the source, but run no analysis"),
+ no_codegen: bool = (false, parse_no_flag, [TRACKED_NO_CRATE_HASH],
+ "run all passes except codegen; no output"),
+ no_generate_arange_section: bool = (false, parse_no_flag, [TRACKED],
+ "omit DWARF address ranges that give faster lookups"),
+ no_interleave_lints: bool = (false, parse_no_flag, [UNTRACKED],
+ "execute lints separately; allows benchmarking individual lints"),
+ no_leak_check: bool = (false, parse_no_flag, [UNTRACKED],
+ "disable the 'leak check' for subtyping; unsound, but useful for tests"),
+ no_link: bool = (false, parse_no_flag, [TRACKED],
+ "compile without linking"),
+ no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED],
+ "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"),
+ no_unique_section_names: bool = (false, parse_bool, [TRACKED],
+ "do not use unique names for text and data sections when -Z function-sections is used"),
+ no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED],
+ "prevent automatic injection of the profiler_builtins crate"),
+ normalize_docs: bool = (false, parse_bool, [TRACKED],
+ "normalize associated items in rustdoc when generating documentation"),
+ oom: OomStrategy = (OomStrategy::Abort, parse_oom_strategy, [TRACKED],
+ "panic strategy for out-of-memory handling"),
+ osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],
+ "pass `-install_name @rpath/...` to the macOS linker (default: no)"),
+ diagnostic_width: Option<usize> = (None, parse_opt_number, [UNTRACKED],
+ "set the current output width for diagnostic truncation"),
+ panic_abort_tests: bool = (false, parse_bool, [TRACKED],
+ "support compiling tests with panic=abort (default: no)"),
+ panic_in_drop: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy, [TRACKED],
+ "panic strategy for panics in drops"),
+ parse_only: bool = (false, parse_bool, [UNTRACKED],
+ "parse only; do not compile, assemble, or link (default: no)"),
+ perf_stats: bool = (false, parse_bool, [UNTRACKED],
+ "print some performance-related statistics (default: no)"),
+ pick_stable_methods_before_any_unstable: bool = (true, parse_bool, [TRACKED],
+ "try to pick stable methods first before picking any unstable methods (default: yes)"),
+ plt: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "whether to use the PLT when calling into shared libraries;
+ only has effect for PIC code on systems with ELF binaries
+ (default: PLT is disabled if full relro is enabled)"),
+ polonius: bool = (false, parse_bool, [TRACKED],
+ "enable polonius-based borrow-checker (default: no)"),
+ polymorphize: bool = (false, parse_bool, [TRACKED],
+ "perform polymorphization analysis"),
+ pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED],
+ "a single extra argument to prepend the linker invocation (can be used several times)"),
+ pre_link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED],
+ "extra arguments to prepend to the linker invocation (space separated)"),
+ precise_enum_drop_elaboration: bool = (true, parse_bool, [TRACKED],
+ "use a more precise version of drop elaboration for matches on enums (default: yes). \
+ This results in better codegen, but has caused miscompilations on some tier 2 platforms. \
+ See #77382 and #74551."),
+ print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
+ "make rustc print the total optimization fuel used by a crate"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::print_llvm_passes` instead of this field"))]
+ print_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
+ "print the LLVM optimization passes being run (default: no)"),
+ print_mono_items: Option<String> = (None, parse_opt_string, [UNTRACKED],
+ "print the result of the monomorphization collection pass"),
+ print_type_sizes: bool = (false, parse_bool, [UNTRACKED],
+ "print layout information for each type encountered (default: no)"),
+ proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
+ "show backtraces for panics during proc-macro execution (default: no)"),
+ proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread,
+ parse_proc_macro_execution_strategy, [UNTRACKED],
+ "how to run proc-macro code (default: same-thread)"),
+ profile: bool = (false, parse_bool, [TRACKED],
+ "insert profiling code (default: no)"),
+ profile_closures: bool = (false, parse_no_flag, [UNTRACKED],
+ "profile size of closures"),
+ profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
+ "file path to emit profiling data at runtime when using 'profile' \
+ (default based on relative source path)"),
+ profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED],
+ "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"),
+ profile_sample_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
+ "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"),
+ query_dep_graph: bool = (false, parse_bool, [UNTRACKED],
+ "enable queries of the dependency graph for regression testing (default: no)"),
+ randomize_layout: bool = (false, parse_bool, [TRACKED],
+ "randomize the layout of types (default: no)"),
+ layout_seed: Option<u64> = (None, parse_opt_number, [TRACKED],
+ "seed layout randomization"),
+ relax_elf_relocations: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "whether ELF relocations can be relaxed"),
+ relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED],
+ "choose which RELRO level to use"),
+ remap_cwd_prefix: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
+ "remap paths under the current working directory to this path prefix"),
+ simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
+ "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \
+ to rust's source base directory. only meant for testing purposes"),
+ report_delayed_bugs: bool = (false, parse_bool, [TRACKED],
+ "immediately print bugs registered with `delay_span_bug` (default: no)"),
+ sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
+ "use a sanitizer"),
+ sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
+ "enable origins tracking in MemorySanitizer"),
+ sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
+ "enable recovery for selected sanitizers"),
+ saturating_float_casts: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "make float->int casts UB-free: numbers outside the integer type's range are clipped to \
+ the max/min integer respectively, and NaN is mapped to 0 (default: yes)"),
+ save_analysis: bool = (false, parse_bool, [UNTRACKED],
+ "write syntax and type analysis (in JSON format) information, in \
+ addition to normal output (default: no)"),
+ self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled,
+ parse_switch_with_opt_path, [UNTRACKED],
+ "run the self profiler and output the raw event data"),
+ /// keep this in sync with the event filter names in librustc_data_structures/profiling.rs
+ self_profile_events: Option<Vec<String>> = (None, parse_opt_comma_list, [UNTRACKED],
+ "specify the events recorded by the self profiler;
+ for example: `-Z self-profile-events=default,query-keys`
+ all options: none, all, default, generic-activity, query-provider, query-cache-hit
+ query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"),
+ self_profile_counter: String = ("wall-time".to_string(), parse_string, [UNTRACKED],
+ "counter used by the self profiler (default: `wall-time`), one of:
+ `wall-time` (monotonic clock, i.e. `std::time::Instant`)
+ `instructions:u` (retired instructions, userspace-only)
+ `instructions-minus-irqs:u` (subtracting hardware interrupt counts for extra accuracy)"
+ ),
+ share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "make the current crate share its generic instantiations"),
+ show_span: Option<String> = (None, parse_opt_string, [TRACKED],
+ "show spans for compiler debugging (expr|pat|ty)"),
+ span_debug: bool = (false, parse_bool, [UNTRACKED],
+ "forward proc_macro::Span's `Debug` impl to `Span`"),
+ /// o/w tests have closure@path
+ span_free_formats: bool = (false, parse_bool, [UNTRACKED],
+ "exclude spans when debug-printing compiler state (default: no)"),
+ src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED],
+ "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field"))]
+ stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED],
+ "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"),
+ strict_init_checks: bool = (false, parse_bool, [TRACKED],
+ "control if mem::uninitialized and mem::zeroed panic on more UB"),
+ strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
+ "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
+ split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED],
+ "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform)
+ (default: `split`)
+
+ `split`: sections which do not require relocation are written into a DWARF object (`.dwo`)
+ file which is ignored by the linker
+ `single`: sections which do not require relocation are written into object file but ignored
+ by the linker"),
+ split_dwarf_inlining: bool = (true, parse_bool, [TRACKED],
+ "provide minimal debug info in the object/executable to facilitate online \
+ symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"),
+ symbol_mangling_version: Option<SymbolManglingVersion> = (None,
+ parse_symbol_mangling_version, [TRACKED],
+ "which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field"))]
+ teach: bool = (false, parse_bool, [TRACKED],
+ "show extended diagnostic help (default: no)"),
+ temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED],
+ "the directory the intermediate files are written to"),
+ // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved
+ // alongside query results and changes to translation options can affect diagnostics - so
+ // translation options should be tracked.
+ translate_lang: Option<LanguageIdentifier> = (None, parse_opt_langid, [TRACKED],
+ "language identifier for diagnostic output"),
+ translate_additional_ftl: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
+ "additional fluent translation to preferentially use (for testing translation)"),
+ translate_directionality_markers: bool = (false, parse_bool, [TRACKED],
+ "emit directionality isolation markers in translated diagnostics"),
+ tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
+ "select processor to schedule for (`rustc --print target-cpus` for details)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))]
+ thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "enable ThinLTO when possible"),
+ thir_unsafeck: bool = (false, parse_bool, [TRACKED],
+ "use the THIR unsafety checker (default: no)"),
+ /// We default to 1 here since we want to behave like
+ /// a sequential compiler for now. This'll likely be adjusted
+ /// in the future. Note that -Zthreads=0 is the way to get
+ /// the num_cpus behavior.
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field"))]
+ threads: usize = (1, parse_threads, [UNTRACKED],
+ "use a thread pool with N threads"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))]
+ time: bool = (false, parse_bool, [UNTRACKED],
+ "measure time of rustc processes (default: no)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_llvm_passes` instead of this field"))]
+ time_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
+ "measure time of each LLVM pass (default: no)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))]
+ time_passes: bool = (false, parse_bool, [UNTRACKED],
+ "measure time of each rustc pass (default: no)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field"))]
+ tls_model: Option<TlsModel> = (None, parse_tls_model, [TRACKED],
+ "choose the TLS model to use (`rustc --print tls-models` for details)"),
+ trace_macros: bool = (false, parse_bool, [UNTRACKED],
+ "for every macro invocation, print its name and arguments (default: no)"),
+ translate_remapped_path_to_local_path: bool = (true, parse_bool, [TRACKED],
+ "translate remapped paths into local paths when possible (default: yes)"),
+ trap_unreachable: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "generate trap instructions for unreachable intrinsics (default: use target setting, usually yes)"),
+ treat_err_as_bug: Option<NonZeroUsize> = (None, parse_treat_err_as_bug, [TRACKED],
+ "treat error number `val` that occurs as bug"),
+ trim_diagnostic_paths: bool = (true, parse_bool, [UNTRACKED],
+ "in diagnostics, use heuristics to shorten paths referring to items"),
+ ui_testing: bool = (false, parse_bool, [UNTRACKED],
+ "emit compiler diagnostics in a form suitable for UI testing (default: no)"),
+ uninit_const_chunk_threshold: usize = (16, parse_number, [TRACKED],
+ "allow generating const initializers with mixed init/uninit chunks, \
+ and set the maximum number of chunks for which this is allowed (default: 16)"),
+ unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED],
+ "take the brakes off const evaluation. NOTE: this is unsound (default: no)"),
+ unpretty: Option<String> = (None, parse_unpretty, [UNTRACKED],
+ "present the input source, unstable (and less-pretty) variants;
+ `normal`, `identified`,
+ `expanded`, `expanded,identified`,
+ `expanded,hygiene` (with internal representations),
+ `ast-tree` (raw AST before expansion),
+ `ast-tree,expanded` (raw AST after expansion),
+ `hir` (the HIR), `hir,identified`,
+ `hir,typed` (HIR with types for each node),
+ `hir-tree` (dump the raw HIR),
+ `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR)"),
+ unsound_mir_opts: bool = (false, parse_bool, [TRACKED],
+ "enable unsound and buggy MIR optimizations (default: no)"),
+ /// This name is kind of confusing: Most unstable options enable something themselves, while
+ /// this just allows "normal" options to be feature-gated.
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::unstable_options` instead of this field"))]
+ unstable_options: bool = (false, parse_bool, [UNTRACKED],
+ "adds unstable command line options to rustc interface (default: no)"),
+ use_ctors_section: Option<bool> = (None, parse_opt_bool, [TRACKED],
+ "use legacy .ctors section for initializers rather than .init_array"),
+ validate_mir: bool = (false, parse_bool, [UNTRACKED],
+ "validate MIR after each transformation"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verbose` instead of this field"))]
+ verbose: bool = (false, parse_bool, [UNTRACKED],
+ "in general, enable more debug printouts (default: no)"),
+ #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verify_llvm_ir` instead of this field"))]
+ verify_llvm_ir: bool = (false, parse_bool, [TRACKED],
+ "verify LLVM IR (default: no)"),
+ virtual_function_elimination: bool = (false, parse_bool, [TRACKED],
+ "enables dead virtual function elimination optimization. \
+ Requires `-Clto[=[fat,yes]]`"),
+ wasi_exec_model: Option<WasiExecModel> = (None, parse_wasi_exec_model, [TRACKED],
+ "whether to build a wasi command or reactor"),
+
+ // This list is in alphabetical order.
+ //
+ // If you add a new option, please update:
+ // - compiler/rustc_interface/src/tests.rs
+}
+
+#[derive(Clone, Hash, PartialEq, Eq, Debug)]
+pub enum WasiExecModel {
+ Command,
+ Reactor,
+}
+
+#[derive(Clone, Copy, Hash)]
+pub enum LdImpl {
+ Lld,
+}
diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs
new file mode 100644
index 000000000..e5e6579d7
--- /dev/null
+++ b/compiler/rustc_session/src/output.rs
@@ -0,0 +1,202 @@
+//! Related to out filenames of compilation (e.g. save analysis, binaries).
+use crate::config::{CrateType, Input, OutputFilenames, OutputType};
+use crate::Session;
+use rustc_ast as ast;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+use std::path::{Path, PathBuf};
+
+pub fn out_filename(
+ sess: &Session,
+ crate_type: CrateType,
+ outputs: &OutputFilenames,
+ crate_name: &str,
+) -> PathBuf {
+ let default_filename = filename_for_input(sess, crate_type, crate_name, outputs);
+ let out_filename = outputs
+ .outputs
+ .get(&OutputType::Exe)
+ .and_then(|s| s.to_owned())
+ .or_else(|| outputs.single_output_file.clone())
+ .unwrap_or(default_filename);
+
+ check_file_is_writeable(&out_filename, sess);
+
+ out_filename
+}
+
+/// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers
+/// check this already -- however, the Linux linker will happily overwrite a
+/// read-only file. We should be consistent.
+pub fn check_file_is_writeable(file: &Path, sess: &Session) {
+ if !is_writeable(file) {
+ sess.fatal(&format!(
+ "output file {} is not writeable -- check its \
+ permissions",
+ file.display()
+ ));
+ }
+}
+
+fn is_writeable(p: &Path) -> bool {
+ match p.metadata() {
+ Err(..) => true,
+ Ok(m) => !m.permissions().readonly(),
+ }
+}
+
+pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input) -> String {
+ let validate = |s: String, span: Option<Span>| {
+ validate_crate_name(sess, &s, span);
+ s
+ };
+
+ // Look in attributes 100% of the time to make sure the attribute is marked
+ // as used. After doing this, however, we still prioritize a crate name from
+ // the command line over one found in the #[crate_name] attribute. If we
+ // find both we ensure that they're the same later on as well.
+ let attr_crate_name =
+ sess.find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s)));
+
+ if let Some(ref s) = sess.opts.crate_name {
+ if let Some((attr, name)) = attr_crate_name {
+ if name.as_str() != s {
+ let msg = format!(
+ "`--crate-name` and `#[crate_name]` are \
+ required to match, but `{s}` != `{name}`"
+ );
+ sess.span_err(attr.span, &msg);
+ }
+ }
+ return validate(s.clone(), None);
+ }
+
+ if let Some((attr, s)) = attr_crate_name {
+ return validate(s.to_string(), Some(attr.span));
+ }
+ if let Input::File(ref path) = *input {
+ if let Some(s) = path.file_stem().and_then(|s| s.to_str()) {
+ if s.starts_with('-') {
+ let msg = format!(
+ "crate names cannot start with a `-`, but \
+ `{s}` has a leading hyphen"
+ );
+ sess.err(&msg);
+ } else {
+ return validate(s.replace('-', "_"), None);
+ }
+ }
+ }
+
+ "rust_out".to_string()
+}
+
+pub fn validate_crate_name(sess: &Session, s: &str, sp: Option<Span>) {
+ let mut err_count = 0;
+ {
+ let mut say = |s: &str| {
+ match sp {
+ Some(sp) => sess.span_err(sp, s),
+ None => sess.err(s),
+ };
+ err_count += 1;
+ };
+ if s.is_empty() {
+ say("crate name must not be empty");
+ }
+ for c in s.chars() {
+ if c.is_alphanumeric() {
+ continue;
+ }
+ if c == '_' {
+ continue;
+ }
+ say(&format!("invalid character `{c}` in crate name: `{s}`"));
+ }
+ }
+
+ if err_count > 0 {
+ sess.abort_if_errors();
+ }
+}
+
+pub fn filename_for_metadata(
+ sess: &Session,
+ crate_name: &str,
+ outputs: &OutputFilenames,
+) -> PathBuf {
+ // If the command-line specified the path, use that directly.
+ if let Some(Some(out_filename)) = sess.opts.output_types.get(&OutputType::Metadata) {
+ return out_filename.clone();
+ }
+
+ let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
+
+ let out_filename = outputs
+ .single_output_file
+ .clone()
+ .unwrap_or_else(|| outputs.out_directory.join(&format!("lib{libname}.rmeta")));
+
+ check_file_is_writeable(&out_filename, sess);
+
+ out_filename
+}
+
+pub fn filename_for_input(
+ sess: &Session,
+ crate_type: CrateType,
+ crate_name: &str,
+ outputs: &OutputFilenames,
+) -> PathBuf {
+ let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
+
+ match crate_type {
+ CrateType::Rlib => outputs.out_directory.join(&format!("lib{libname}.rlib")),
+ CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => {
+ let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix);
+ outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))
+ }
+ CrateType::Staticlib => {
+ let (prefix, suffix) = (&sess.target.staticlib_prefix, &sess.target.staticlib_suffix);
+ outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))
+ }
+ CrateType::Executable => {
+ let suffix = &sess.target.exe_suffix;
+ let out_filename = outputs.path(OutputType::Exe);
+ if suffix.is_empty() { out_filename } else { out_filename.with_extension(&suffix[1..]) }
+ }
+ }
+}
+
+/// Returns default crate type for target
+///
+/// Default crate type is used when crate type isn't provided neither
+/// through cmd line arguments nor through crate attributes
+///
+/// It is CrateType::Executable for all platforms but iOS as there is no
+/// way to run iOS binaries anyway without jailbreaking and
+/// interaction with Rust code through static library is the only
+/// option for now
+pub fn default_output_for_target(sess: &Session) -> CrateType {
+ if !sess.target.executables { CrateType::Staticlib } else { CrateType::Executable }
+}
+
+/// Checks if target supports crate_type as output
+pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool {
+ if let CrateType::Cdylib | CrateType::Dylib | CrateType::ProcMacro = crate_type {
+ if !sess.target.dynamic_linking {
+ return true;
+ }
+ if sess.crt_static(Some(crate_type)) && !sess.target.crt_static_allows_dylibs {
+ return true;
+ }
+ }
+ if let CrateType::ProcMacro | CrateType::Dylib = crate_type && sess.target.only_cdylib {
+ return true;
+ }
+ if let CrateType::Executable = crate_type && !sess.target.executables {
+ return true;
+ }
+
+ false
+}
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
new file mode 100644
index 000000000..f31d52147
--- /dev/null
+++ b/compiler/rustc_session/src/parse.rs
@@ -0,0 +1,326 @@
+//! Contains `ParseSess` which holds state living beyond what one `Parser` might.
+//! It also serves as an input to the parser itself.
+
+use crate::config::CheckCfg;
+use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId};
+use crate::SessionDiagnostic;
+use rustc_ast::node_id::NodeId;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::sync::{Lock, Lrc};
+use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
+use rustc_errors::{
+ error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder,
+ DiagnosticMessage, ErrorGuaranteed, MultiSpan,
+};
+use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
+use rustc_span::edition::Edition;
+use rustc_span::hygiene::ExpnId;
+use rustc_span::source_map::{FilePathMapping, SourceMap};
+use rustc_span::{Span, Symbol};
+
+use std::str;
+
+/// The set of keys (and, optionally, values) that define the compilation
+/// environment of the crate, used to drive conditional compilation.
+pub type CrateConfig = FxHashSet<(Symbol, Option<Symbol>)>;
+pub type CrateCheckConfig = CheckCfg<Symbol>;
+
+/// Collected spans during parsing for places where a certain feature was
+/// used and should be feature gated accordingly in `check_crate`.
+#[derive(Default)]
+pub struct GatedSpans {
+ pub spans: Lock<FxHashMap<Symbol, Vec<Span>>>,
+}
+
+impl GatedSpans {
+ /// Feature gate the given `span` under the given `feature`
+ /// which is same `Symbol` used in `active.rs`.
+ pub fn gate(&self, feature: Symbol, span: Span) {
+ self.spans.borrow_mut().entry(feature).or_default().push(span);
+ }
+
+ /// Ungate the last span under the given `feature`.
+ /// Panics if the given `span` wasn't the last one.
+ ///
+ /// Using this is discouraged unless you have a really good reason to.
+ pub fn ungate_last(&self, feature: Symbol, span: Span) {
+ let removed_span = self.spans.borrow_mut().entry(feature).or_default().pop().unwrap();
+ debug_assert_eq!(span, removed_span);
+ }
+
+ /// Is the provided `feature` gate ungated currently?
+ ///
+ /// Using this is discouraged unless you have a really good reason to.
+ pub fn is_ungated(&self, feature: Symbol) -> bool {
+ self.spans.borrow().get(&feature).map_or(true, |spans| spans.is_empty())
+ }
+
+ /// Prepend the given set of `spans` onto the set in `self`.
+ pub fn merge(&self, mut spans: FxHashMap<Symbol, Vec<Span>>) {
+ let mut inner = self.spans.borrow_mut();
+ for (gate, mut gate_spans) in inner.drain() {
+ spans.entry(gate).or_default().append(&mut gate_spans);
+ }
+ *inner = spans;
+ }
+}
+
+#[derive(Default)]
+pub struct SymbolGallery {
+ /// All symbols occurred and their first occurrence span.
+ pub symbols: Lock<FxHashMap<Symbol, Span>>,
+}
+
+impl SymbolGallery {
+ /// Insert a symbol and its span into symbol gallery.
+ /// If the symbol has occurred before, ignore the new occurrence.
+ pub fn insert(&self, symbol: Symbol, span: Span) {
+ self.symbols.lock().entry(symbol).or_insert(span);
+ }
+}
+
+/// Construct a diagnostic for a language feature error due to the given `span`.
+/// The `feature`'s `Symbol` is the one you used in `active.rs` and `rustc_span::symbols`.
+pub fn feature_err<'a>(
+ sess: &'a ParseSess,
+ feature: Symbol,
+ span: impl Into<MultiSpan>,
+ explain: &str,
+) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+ feature_err_issue(sess, feature, span, GateIssue::Language, explain)
+}
+
+/// Construct a diagnostic for a feature gate error.
+///
+/// This variant allows you to control whether it is a library or language feature.
+/// Almost always, you want to use this for a language feature. If so, prefer `feature_err`.
+pub fn feature_err_issue<'a>(
+ sess: &'a ParseSess,
+ feature: Symbol,
+ span: impl Into<MultiSpan>,
+ issue: GateIssue,
+ explain: &str,
+) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+ let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658));
+ add_feature_diagnostics_for_issue(&mut err, sess, feature, issue);
+ err
+}
+
+/// Adds the diagnostics for a feature to an existing error.
+pub fn add_feature_diagnostics<'a>(err: &mut Diagnostic, sess: &'a ParseSess, feature: Symbol) {
+ add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language);
+}
+
+/// Adds the diagnostics for a feature to an existing error.
+///
+/// This variant allows you to control whether it is a library or language feature.
+/// Almost always, you want to use this for a language feature. If so, prefer
+/// `add_feature_diagnostics`.
+pub fn add_feature_diagnostics_for_issue<'a>(
+ err: &mut Diagnostic,
+ sess: &'a ParseSess,
+ feature: Symbol,
+ issue: GateIssue,
+) {
+ if let Some(n) = find_feature_issue(feature, issue) {
+ err.note(&format!(
+ "see issue #{n} <https://github.com/rust-lang/rust/issues/{n}> for more information"
+ ));
+ }
+
+ // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
+ if sess.unstable_features.is_nightly_build() {
+ err.help(&format!("add `#![feature({feature})]` to the crate attributes to enable"));
+ }
+}
+
+/// Info about a parsing session.
+pub struct ParseSess {
+ pub span_diagnostic: Handler,
+ pub unstable_features: UnstableFeatures,
+ pub config: CrateConfig,
+ pub check_config: CrateCheckConfig,
+ pub edition: Edition,
+ /// Places where raw identifiers were used. This is used to avoid complaining about idents
+ /// clashing with keywords in new editions.
+ pub raw_identifier_spans: Lock<Vec<Span>>,
+ /// Places where identifiers that contain invalid Unicode codepoints but that look like they
+ /// should be. Useful to avoid bad tokenization when encountering emoji. We group them to
+ /// provide a single error per unique incorrect identifier.
+ pub bad_unicode_identifiers: Lock<FxHashMap<Symbol, Vec<Span>>>,
+ source_map: Lrc<SourceMap>,
+ pub buffered_lints: Lock<Vec<BufferedEarlyLint>>,
+ /// Contains the spans of block expressions that could have been incomplete based on the
+ /// operation token that followed it, but that the parser cannot identify without further
+ /// analysis.
+ pub ambiguous_block_expr_parse: Lock<FxHashMap<Span, Span>>,
+ pub gated_spans: GatedSpans,
+ pub symbol_gallery: SymbolGallery,
+ /// The parser has reached `Eof` due to an unclosed brace. Used to silence unnecessary errors.
+ pub reached_eof: Lock<bool>,
+ /// Environment variables accessed during the build and their values when they exist.
+ pub env_depinfo: Lock<FxHashSet<(Symbol, Option<Symbol>)>>,
+ /// File paths accessed during the build.
+ pub file_depinfo: Lock<FxHashSet<Symbol>>,
+ /// All the type ascriptions expressions that have had a suggestion for likely path typo.
+ pub type_ascription_path_suggestions: Lock<FxHashSet<Span>>,
+ /// Whether cfg(version) should treat the current release as incomplete
+ pub assume_incomplete_release: bool,
+ /// Spans passed to `proc_macro::quote_span`. Each span has a numerical
+ /// identifier represented by its position in the vector.
+ pub proc_macro_quoted_spans: Lock<Vec<Span>>,
+}
+
+impl ParseSess {
+ /// Used for testing.
+ pub fn new(file_path_mapping: FilePathMapping) -> Self {
+ let fallback_bundle = fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let sm = Lrc::new(SourceMap::new(file_path_mapping));
+ let handler = Handler::with_tty_emitter(
+ ColorConfig::Auto,
+ true,
+ None,
+ Some(sm.clone()),
+ None,
+ fallback_bundle,
+ );
+ ParseSess::with_span_handler(handler, sm)
+ }
+
+ pub fn with_span_handler(handler: Handler, source_map: Lrc<SourceMap>) -> Self {
+ Self {
+ span_diagnostic: handler,
+ unstable_features: UnstableFeatures::from_environment(None),
+ config: FxHashSet::default(),
+ check_config: CrateCheckConfig::default(),
+ edition: ExpnId::root().expn_data().edition,
+ raw_identifier_spans: Lock::new(Vec::new()),
+ bad_unicode_identifiers: Lock::new(Default::default()),
+ source_map,
+ buffered_lints: Lock::new(vec![]),
+ ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
+ gated_spans: GatedSpans::default(),
+ symbol_gallery: SymbolGallery::default(),
+ reached_eof: Lock::new(false),
+ env_depinfo: Default::default(),
+ file_depinfo: Default::default(),
+ type_ascription_path_suggestions: Default::default(),
+ assume_incomplete_release: false,
+ proc_macro_quoted_spans: Default::default(),
+ }
+ }
+
+ pub fn with_silent_emitter(fatal_note: Option<String>) -> Self {
+ let fallback_bundle = fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+ let fatal_handler =
+ Handler::with_tty_emitter(ColorConfig::Auto, false, None, None, None, fallback_bundle);
+ let handler = Handler::with_emitter(
+ false,
+ None,
+ Box::new(SilentEmitter { fatal_handler, fatal_note }),
+ );
+ ParseSess::with_span_handler(handler, sm)
+ }
+
+ #[inline]
+ pub fn source_map(&self) -> &SourceMap {
+ &self.source_map
+ }
+
+ pub fn clone_source_map(&self) -> Lrc<SourceMap> {
+ self.source_map.clone()
+ }
+
+ pub fn buffer_lint(
+ &self,
+ lint: &'static Lint,
+ span: impl Into<MultiSpan>,
+ node_id: NodeId,
+ msg: &str,
+ ) {
+ self.buffered_lints.with_lock(|buffered_lints| {
+ buffered_lints.push(BufferedEarlyLint {
+ span: span.into(),
+ node_id,
+ msg: msg.into(),
+ lint_id: LintId::of(lint),
+ diagnostic: BuiltinLintDiagnostics::Normal,
+ });
+ });
+ }
+
+ pub fn buffer_lint_with_diagnostic(
+ &self,
+ lint: &'static Lint,
+ span: impl Into<MultiSpan>,
+ node_id: NodeId,
+ msg: &str,
+ diagnostic: BuiltinLintDiagnostics,
+ ) {
+ self.buffered_lints.with_lock(|buffered_lints| {
+ buffered_lints.push(BufferedEarlyLint {
+ span: span.into(),
+ node_id,
+ msg: msg.into(),
+ lint_id: LintId::of(lint),
+ diagnostic,
+ });
+ });
+ }
+
+ /// Extend an error with a suggestion to wrap an expression with parentheses to allow the
+ /// parser to continue parsing the following operation as part of the same expression.
+ pub fn expr_parentheses_needed(&self, err: &mut Diagnostic, span: Span) {
+ err.multipart_suggestion(
+ "parentheses are required to parse this as an expression",
+ vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), ")".to_string())],
+ Applicability::MachineApplicable,
+ );
+ }
+
+ pub fn save_proc_macro_span(&self, span: Span) -> usize {
+ let mut spans = self.proc_macro_quoted_spans.lock();
+ spans.push(span);
+ return spans.len() - 1;
+ }
+
+ pub fn proc_macro_quoted_spans(&self) -> Vec<Span> {
+ self.proc_macro_quoted_spans.lock().clone()
+ }
+
+ pub fn create_err<'a>(
+ &'a self,
+ err: impl SessionDiagnostic<'a>,
+ ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+ err.into_diagnostic(self)
+ }
+
+ pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
+ self.create_err(err).emit()
+ }
+
+ pub fn create_warning<'a>(
+ &'a self,
+ warning: impl SessionDiagnostic<'a, ()>,
+ ) -> DiagnosticBuilder<'a, ()> {
+ warning.into_diagnostic(self)
+ }
+
+ pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) {
+ self.create_warning(warning).emit()
+ }
+
+ #[rustc_lint_diagnostics]
+ pub fn struct_err(
+ &self,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+ self.span_diagnostic.struct_err(msg)
+ }
+
+ #[rustc_lint_diagnostics]
+ pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
+ self.span_diagnostic.struct_warn(msg)
+ }
+}
diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs
new file mode 100644
index 000000000..56a6b6f3b
--- /dev/null
+++ b/compiler/rustc_session/src/search_paths.rs
@@ -0,0 +1,93 @@
+use crate::filesearch::make_target_lib_path;
+use crate::{config, early_error};
+use std::path::{Path, PathBuf};
+
+#[derive(Clone, Debug)]
+pub struct SearchPath {
+ pub kind: PathKind,
+ pub dir: PathBuf,
+ pub files: Vec<SearchPathFile>,
+}
+
+/// The obvious implementation of `SearchPath::files` is a `Vec<PathBuf>`. But
+/// it is searched repeatedly by `find_library_crate`, and the searches involve
+/// checking the prefix and suffix of the filename of each `PathBuf`. This is
+/// doable, but very slow, because it involves calls to `file_name` and
+/// `extension` that are themselves slow.
+///
+/// This type augments the `PathBuf` with an `String` containing the
+/// `PathBuf`'s filename. The prefix and suffix checking is much faster on the
+/// `String` than the `PathBuf`. (The filename must be valid UTF-8. If it's
+/// not, the entry should be skipped, because all Rust output files are valid
+/// UTF-8, and so a non-UTF-8 filename couldn't be one we're looking for.)
+#[derive(Clone, Debug)]
+pub struct SearchPathFile {
+ pub path: PathBuf,
+ pub file_name_str: String,
+}
+
+#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable, HashStable_Generic)]
+pub enum PathKind {
+ Native,
+ Crate,
+ Dependency,
+ Framework,
+ ExternFlag,
+ All,
+}
+
+impl PathKind {
+ pub fn matches(&self, kind: PathKind) -> bool {
+ match (self, kind) {
+ (PathKind::All, _) | (_, PathKind::All) => true,
+ _ => *self == kind,
+ }
+ }
+}
+
+impl SearchPath {
+ pub fn from_cli_opt(path: &str, output: config::ErrorOutputType) -> Self {
+ let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") {
+ (PathKind::Native, stripped)
+ } else if let Some(stripped) = path.strip_prefix("crate=") {
+ (PathKind::Crate, stripped)
+ } else if let Some(stripped) = path.strip_prefix("dependency=") {
+ (PathKind::Dependency, stripped)
+ } else if let Some(stripped) = path.strip_prefix("framework=") {
+ (PathKind::Framework, stripped)
+ } else if let Some(stripped) = path.strip_prefix("all=") {
+ (PathKind::All, stripped)
+ } else {
+ (PathKind::All, path)
+ };
+ if path.is_empty() {
+ early_error(output, "empty search path given via `-L`");
+ }
+
+ let dir = PathBuf::from(path);
+ Self::new(kind, dir)
+ }
+
+ pub fn from_sysroot_and_triple(sysroot: &Path, triple: &str) -> Self {
+ Self::new(PathKind::All, make_target_lib_path(sysroot, triple))
+ }
+
+ fn new(kind: PathKind, dir: PathBuf) -> Self {
+ // Get the files within the directory.
+ let files = match std::fs::read_dir(&dir) {
+ Ok(files) => files
+ .filter_map(|e| {
+ e.ok().and_then(|e| {
+ e.file_name().to_str().map(|s| SearchPathFile {
+ path: e.path(),
+ file_name_str: s.to_string(),
+ })
+ })
+ })
+ .collect::<Vec<_>>(),
+ Err(..) => vec![],
+ };
+
+ SearchPath { kind, dir, files }
+ }
+}
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
new file mode 100644
index 000000000..9669287b3
--- /dev/null
+++ b/compiler/rustc_session/src/session.rs
@@ -0,0 +1,1599 @@
+use crate::cgu_reuse_tracker::CguReuseTracker;
+use crate::code_stats::CodeStats;
+pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
+use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath};
+use crate::parse::{add_feature_diagnostics, ParseSess};
+use crate::search_paths::{PathKind, SearchPath};
+use crate::{filesearch, lint};
+
+pub use rustc_ast::attr::MarkedAttrs;
+pub use rustc_ast::Attribute;
+use rustc_data_structures::flock;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::jobserver::{self, Client};
+use rustc_data_structures::profiling::{duration_to_secs_str, SelfProfiler, SelfProfilerRef};
+use rustc_data_structures::sync::{
+ self, AtomicU64, AtomicUsize, Lock, Lrc, OnceCell, OneThread, Ordering, Ordering::SeqCst,
+};
+use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter;
+use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType};
+use rustc_errors::json::JsonEmitter;
+use rustc_errors::registry::Registry;
+use rustc_errors::{
+ fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, EmissionGuarantee,
+ ErrorGuaranteed, FluentBundle, LazyFallbackBundle, MultiSpan,
+};
+use rustc_macros::HashStable_Generic;
+pub use rustc_span::def_id::StableCrateId;
+use rustc_span::edition::Edition;
+use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMap, Span};
+use rustc_span::{sym, SourceFileHashAlgorithm, Symbol};
+use rustc_target::asm::InlineAsmArch;
+use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel};
+use rustc_target::spec::{
+ SanitizerSet, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel,
+};
+
+use std::cell::{self, RefCell};
+use std::env;
+use std::fmt;
+use std::io::Write;
+use std::ops::{Div, Mul};
+use std::path::{Path, PathBuf};
+use std::str::FromStr;
+use std::sync::Arc;
+use std::time::Duration;
+
+pub struct OptimizationFuel {
+ /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`.
+ remaining: u64,
+ /// We're rejecting all further optimizations.
+ out_of_fuel: bool,
+}
+
+/// The behavior of the CTFE engine when an error occurs with regards to backtraces.
+#[derive(Clone, Copy)]
+pub enum CtfeBacktrace {
+ /// Do nothing special, return the error as usual without a backtrace.
+ Disabled,
+ /// Capture a backtrace at the point the error is created and return it in the error
+ /// (to be printed later if/when the error ever actually gets shown to the user).
+ Capture,
+ /// Capture a backtrace at the point the error is created and immediately print it out.
+ Immediate,
+}
+
+/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against
+/// limits are consistent throughout the compiler.
+#[derive(Clone, Copy, Debug, HashStable_Generic)]
+pub struct Limit(pub usize);
+
+impl Limit {
+ /// Create a new limit from a `usize`.
+ pub fn new(value: usize) -> Self {
+ Limit(value)
+ }
+
+ /// Check that `value` is within the limit. Ensures that the same comparisons are used
+ /// throughout the compiler, as mismatches can cause ICEs, see #72540.
+ #[inline]
+ pub fn value_within_limit(&self, value: usize) -> bool {
+ value <= self.0
+ }
+}
+
+impl From<usize> for Limit {
+ fn from(value: usize) -> Self {
+ Self::new(value)
+ }
+}
+
+impl fmt::Display for Limit {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl Div<usize> for Limit {
+ type Output = Limit;
+
+ fn div(self, rhs: usize) -> Self::Output {
+ Limit::new(self.0 / rhs)
+ }
+}
+
+impl Mul<usize> for Limit {
+ type Output = Limit;
+
+ fn mul(self, rhs: usize) -> Self::Output {
+ Limit::new(self.0 * rhs)
+ }
+}
+
+#[derive(Clone, Copy, Debug, HashStable_Generic)]
+pub struct Limits {
+ /// The maximum recursion limit for potentially infinitely recursive
+ /// operations such as auto-dereference and monomorphization.
+ pub recursion_limit: Limit,
+ /// The size at which the `large_assignments` lint starts
+ /// being emitted.
+ pub move_size_limit: Limit,
+ /// The maximum length of types during monomorphization.
+ pub type_length_limit: Limit,
+ /// The maximum blocks a const expression can evaluate.
+ pub const_eval_limit: Limit,
+}
+
+/// Represents the data associated with a compilation
+/// session for a single crate.
+pub struct Session {
+ pub target: Target,
+ pub host: Target,
+ pub opts: config::Options,
+ pub host_tlib_path: Lrc<SearchPath>,
+ pub target_tlib_path: Lrc<SearchPath>,
+ pub parse_sess: ParseSess,
+ pub sysroot: PathBuf,
+ /// The name of the root source file of the crate, in the local file system.
+ /// `None` means that there is no source file.
+ pub local_crate_source_file: Option<PathBuf>,
+
+ crate_types: OnceCell<Vec<CrateType>>,
+ /// The `stable_crate_id` is constructed out of the crate name and all the
+ /// `-C metadata` arguments passed to the compiler. Its value forms a unique
+ /// global identifier for the crate. It is used to allow multiple crates
+ /// with the same name to coexist. See the
+ /// `rustc_codegen_llvm::back::symbol_names` module for more information.
+ pub stable_crate_id: OnceCell<StableCrateId>,
+
+ features: OnceCell<rustc_feature::Features>,
+
+ incr_comp_session: OneThread<RefCell<IncrCompSession>>,
+ /// Used for incremental compilation tests. Will only be populated if
+ /// `-Zquery-dep-graph` is specified.
+ pub cgu_reuse_tracker: CguReuseTracker,
+
+ /// Used by `-Z self-profile`.
+ pub prof: SelfProfilerRef,
+
+ /// Some measurements that are being gathered during compilation.
+ pub perf_stats: PerfStats,
+
+ /// Data about code being compiled, gathered during compilation.
+ pub code_stats: CodeStats,
+
+ /// Tracks fuel info if `-zfuel=crate=n` is specified.
+ optimization_fuel: Lock<OptimizationFuel>,
+
+ /// Always set to zero and incremented so that we can print fuel expended by a crate.
+ pub print_fuel: AtomicU64,
+
+ /// Loaded up early on in the initialization of this `Session` to avoid
+ /// false positives about a job server in our environment.
+ pub jobserver: Client,
+
+ /// Cap lint level specified by a driver specifically.
+ pub driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
+
+ /// Tracks the current behavior of the CTFE engine when an error occurs.
+ /// Options range from returning the error without a backtrace to returning an error
+ /// and immediately printing the backtrace to stderr.
+ /// The `Lock` is only used by miri to allow setting `ctfe_backtrace` after analysis when
+ /// `MIRI_BACKTRACE` is set. This makes it only apply to miri's errors and not to all CTFE
+ /// errors.
+ pub ctfe_backtrace: Lock<CtfeBacktrace>,
+
+ /// This tracks where `-Zunleash-the-miri-inside-of-you` was used to get around a
+ /// const check, optionally with the relevant feature gate. We use this to
+ /// warn about unleashing, but with a single diagnostic instead of dozens that
+ /// drown everything else in noise.
+ miri_unleashed_features: Lock<Vec<(Span, Option<Symbol>)>>,
+
+ /// Architecture to use for interpreting asm!.
+ pub asm_arch: Option<InlineAsmArch>,
+
+ /// Set of enabled features for the current target.
+ pub target_features: FxHashSet<Symbol>,
+
+ /// Set of enabled features for the current target, including unstable ones.
+ pub unstable_target_features: FxHashSet<Symbol>,
+}
+
+pub struct PerfStats {
+ /// The accumulated time spent on computing symbol hashes.
+ pub symbol_hash_time: Lock<Duration>,
+ /// Total number of values canonicalized queries constructed.
+ pub queries_canonicalized: AtomicUsize,
+ /// Number of times this query is invoked.
+ pub normalize_generic_arg_after_erasing_regions: AtomicUsize,
+ /// Number of times this query is invoked.
+ pub normalize_projection_ty: AtomicUsize,
+}
+
+/// Trait implemented by error types. This should not be implemented manually. Instead, use
+/// `#[derive(SessionDiagnostic)]` -- see [rustc_macros::SessionDiagnostic].
+#[rustc_diagnostic_item = "SessionDiagnostic"]
+pub trait SessionDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> {
+ /// Write out as a diagnostic out of `sess`.
+ #[must_use]
+ fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, T>;
+}
+
+impl Session {
+ pub fn miri_unleashed_feature(&self, span: Span, feature_gate: Option<Symbol>) {
+ self.miri_unleashed_features.lock().push((span, feature_gate));
+ }
+
+ fn check_miri_unleashed_features(&self) {
+ let unleashed_features = self.miri_unleashed_features.lock();
+ if !unleashed_features.is_empty() {
+ let mut must_err = false;
+ // Create a diagnostic pointing at where things got unleashed.
+ let mut diag = self.struct_warn("skipping const checks");
+ for &(span, feature_gate) in unleashed_features.iter() {
+ // FIXME: `span_label` doesn't do anything, so we use "help" as a hack.
+ if let Some(gate) = feature_gate {
+ diag.span_help(span, &format!("skipping check for `{gate}` feature"));
+ // The unleash flag must *not* be used to just "hack around" feature gates.
+ must_err = true;
+ } else {
+ diag.span_help(span, "skipping check that does not even have a feature gate");
+ }
+ }
+ diag.emit();
+ // If we should err, make sure we did.
+ if must_err && self.has_errors().is_none() {
+ // We have skipped a feature gate, and not run into other errors... reject.
+ self.err(
+ "`-Zunleash-the-miri-inside-of-you` may not be used to circumvent feature \
+ gates, except when testing error paths in the CTFE engine",
+ );
+ }
+ }
+ }
+
+ /// Invoked all the way at the end to finish off diagnostics printing.
+ pub fn finish_diagnostics(&self, registry: &Registry) {
+ self.check_miri_unleashed_features();
+ self.diagnostic().print_error_count(registry);
+ self.emit_future_breakage();
+ }
+
+ fn emit_future_breakage(&self) {
+ if !self.opts.json_future_incompat {
+ return;
+ }
+
+ let diags = self.diagnostic().take_future_breakage_diagnostics();
+ if diags.is_empty() {
+ return;
+ }
+ self.parse_sess.span_diagnostic.emit_future_breakage_report(diags);
+ }
+
+ pub fn local_stable_crate_id(&self) -> StableCrateId {
+ self.stable_crate_id.get().copied().unwrap()
+ }
+
+ pub fn crate_types(&self) -> &[CrateType] {
+ self.crate_types.get().unwrap().as_slice()
+ }
+
+ pub fn init_crate_types(&self, crate_types: Vec<CrateType>) {
+ self.crate_types.set(crate_types).expect("`crate_types` was initialized twice")
+ }
+
+ #[rustc_lint_diagnostics]
+ pub fn struct_span_warn<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> DiagnosticBuilder<'_, ()> {
+ self.diagnostic().struct_span_warn(sp, msg)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_span_warn_with_expectation<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ id: lint::LintExpectationId,
+ ) -> DiagnosticBuilder<'_, ()> {
+ self.diagnostic().struct_span_warn_with_expectation(sp, msg, id)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ code: DiagnosticId,
+ ) -> DiagnosticBuilder<'_, ()> {
+ self.diagnostic().struct_span_warn_with_code(sp, msg, code)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
+ self.diagnostic().struct_warn(msg)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_warn_with_expectation(
+ &self,
+ msg: impl Into<DiagnosticMessage>,
+ id: lint::LintExpectationId,
+ ) -> DiagnosticBuilder<'_, ()> {
+ self.diagnostic().struct_warn_with_expectation(msg, id)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_span_allow<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> DiagnosticBuilder<'_, ()> {
+ self.diagnostic().struct_span_allow(sp, msg)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
+ self.diagnostic().struct_allow(msg)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_expect(
+ &self,
+ msg: impl Into<DiagnosticMessage>,
+ id: lint::LintExpectationId,
+ ) -> DiagnosticBuilder<'_, ()> {
+ self.diagnostic().struct_expect(msg, id)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_span_err<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+ self.diagnostic().struct_span_err(sp, msg)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_span_err_with_code<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ code: DiagnosticId,
+ ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+ self.diagnostic().struct_span_err_with_code(sp, msg, code)
+ }
+ // FIXME: This method should be removed (every error should have an associated error code).
+ #[rustc_lint_diagnostics]
+ pub fn struct_err(
+ &self,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+ self.parse_sess.struct_err(msg)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_err_with_code(
+ &self,
+ msg: impl Into<DiagnosticMessage>,
+ code: DiagnosticId,
+ ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+ self.diagnostic().struct_err_with_code(msg, code)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_warn_with_code(
+ &self,
+ msg: impl Into<DiagnosticMessage>,
+ code: DiagnosticId,
+ ) -> DiagnosticBuilder<'_, ()> {
+ self.diagnostic().struct_warn_with_code(msg, code)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_span_fatal<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> DiagnosticBuilder<'_, !> {
+ self.diagnostic().struct_span_fatal(sp, msg)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ code: DiagnosticId,
+ ) -> DiagnosticBuilder<'_, !> {
+ self.diagnostic().struct_span_fatal_with_code(sp, msg, code)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
+ self.diagnostic().struct_fatal(msg)
+ }
+
+ #[rustc_lint_diagnostics]
+ pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! {
+ self.diagnostic().span_fatal(sp, msg)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn span_fatal_with_code<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ code: DiagnosticId,
+ ) -> ! {
+ self.diagnostic().span_fatal_with_code(sp, msg, code)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! {
+ self.diagnostic().fatal(msg).raise()
+ }
+ #[rustc_lint_diagnostics]
+ pub fn span_err_or_warn<S: Into<MultiSpan>>(
+ &self,
+ is_warning: bool,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ ) {
+ if is_warning {
+ self.span_warn(sp, msg);
+ } else {
+ self.span_err(sp, msg);
+ }
+ }
+ #[rustc_lint_diagnostics]
+ pub fn span_err<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> ErrorGuaranteed {
+ self.diagnostic().span_err(sp, msg)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn span_err_with_code<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ code: DiagnosticId,
+ ) {
+ self.diagnostic().span_err_with_code(sp, msg, code)
+ }
+ #[rustc_lint_diagnostics]
+ pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
+ self.diagnostic().err(msg)
+ }
+ pub fn create_err<'a>(
+ &'a self,
+ err: impl SessionDiagnostic<'a>,
+ ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+ self.parse_sess.create_err(err)
+ }
+ pub fn create_feature_err<'a>(
+ &'a self,
+ err: impl SessionDiagnostic<'a>,
+ feature: Symbol,
+ ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+ let mut err = self.parse_sess.create_err(err);
+ add_feature_diagnostics(&mut err, &self.parse_sess, feature);
+ err
+ }
+ pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
+ self.parse_sess.emit_err(err)
+ }
+ pub fn create_warning<'a>(
+ &'a self,
+ err: impl SessionDiagnostic<'a, ()>,
+ ) -> DiagnosticBuilder<'a, ()> {
+ self.parse_sess.create_warning(err)
+ }
+ pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) {
+ self.parse_sess.emit_warning(warning)
+ }
+ #[inline]
+ pub fn err_count(&self) -> usize {
+ self.diagnostic().err_count()
+ }
+ pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
+ self.diagnostic().has_errors()
+ }
+ pub fn has_errors_or_delayed_span_bugs(&self) -> bool {
+ self.diagnostic().has_errors_or_delayed_span_bugs()
+ }
+ pub fn abort_if_errors(&self) {
+ self.diagnostic().abort_if_errors();
+ }
+ pub fn compile_status(&self) -> Result<(), ErrorGuaranteed> {
+ if let Some(reported) = self.diagnostic().has_errors_or_lint_errors() {
+ let _ = self.diagnostic().emit_stashed_diagnostics();
+ Err(reported)
+ } else {
+ Ok(())
+ }
+ }
+ // FIXME(matthewjasper) Remove this method, it should never be needed.
+ pub fn track_errors<F, T>(&self, f: F) -> Result<T, ErrorGuaranteed>
+ where
+ F: FnOnce() -> T,
+ {
+ let old_count = self.err_count();
+ let result = f();
+ if self.err_count() == old_count {
+ Ok(result)
+ } else {
+ Err(ErrorGuaranteed::unchecked_claim_error_was_emitted())
+ }
+ }
+ pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) {
+ self.diagnostic().span_warn(sp, msg)
+ }
+ pub fn span_warn_with_code<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ code: DiagnosticId,
+ ) {
+ self.diagnostic().span_warn_with_code(sp, msg, code)
+ }
+ pub fn warn(&self, msg: impl Into<DiagnosticMessage>) {
+ self.diagnostic().warn(msg)
+ }
+ /// Delay a span_bug() call until abort_if_errors()
+ #[track_caller]
+ pub fn delay_span_bug<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> ErrorGuaranteed {
+ self.diagnostic().delay_span_bug(sp, msg)
+ }
+
+ /// Used for code paths of expensive computations that should only take place when
+ /// warnings or errors are emitted. If no messages are emitted ("good path"), then
+ /// it's likely a bug.
+ pub fn delay_good_path_bug(&self, msg: impl Into<DiagnosticMessage>) {
+ if self.opts.unstable_opts.print_type_sizes
+ || self.opts.unstable_opts.query_dep_graph
+ || self.opts.unstable_opts.dump_mir.is_some()
+ || self.opts.unstable_opts.unpretty.is_some()
+ || self.opts.output_types.contains_key(&OutputType::Mir)
+ || std::env::var_os("RUSTC_LOG").is_some()
+ {
+ return;
+ }
+
+ self.diagnostic().delay_good_path_bug(msg)
+ }
+
+ pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) {
+ self.diagnostic().note_without_error(msg)
+ }
+ pub fn span_note_without_error<S: Into<MultiSpan>>(
+ &self,
+ sp: S,
+ msg: impl Into<DiagnosticMessage>,
+ ) {
+ self.diagnostic().span_note_without_error(sp, msg)
+ }
+ pub fn struct_note_without_error(
+ &self,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> DiagnosticBuilder<'_, ()> {
+ self.diagnostic().struct_note_without_error(msg)
+ }
+
+ #[inline]
+ pub fn diagnostic(&self) -> &rustc_errors::Handler {
+ &self.parse_sess.span_diagnostic
+ }
+
+ #[inline]
+ pub fn source_map(&self) -> &SourceMap {
+ self.parse_sess.source_map()
+ }
+
+ pub fn time_passes(&self) -> bool {
+ self.opts.time_passes()
+ }
+
+ /// Returns `true` if internal lints should be added to the lint store - i.e. if
+ /// `-Zunstable-options` is provided and this isn't rustdoc (internal lints can trigger errors
+ /// to be emitted under rustdoc).
+ pub fn enable_internal_lints(&self) -> bool {
+ self.unstable_options() && !self.opts.actually_rustdoc
+ }
+
+ pub fn instrument_coverage(&self) -> bool {
+ self.opts.cg.instrument_coverage() != InstrumentCoverage::Off
+ }
+
+ pub fn instrument_coverage_except_unused_generics(&self) -> bool {
+ self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics
+ }
+
+ pub fn instrument_coverage_except_unused_functions(&self) -> bool {
+ self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedFunctions
+ }
+
+ /// Gets the features enabled for the current compilation session.
+ /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents
+ /// dependency tracking. Use tcx.features() instead.
+ #[inline]
+ pub fn features_untracked(&self) -> &rustc_feature::Features {
+ self.features.get().unwrap()
+ }
+
+ pub fn init_features(&self, features: rustc_feature::Features) {
+ match self.features.set(features) {
+ Ok(()) => {}
+ Err(_) => panic!("`features` was initialized twice"),
+ }
+ }
+
+ pub fn is_sanitizer_cfi_enabled(&self) -> bool {
+ self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
+ }
+
+ /// Check whether this compile session and crate type use static crt.
+ pub fn crt_static(&self, crate_type: Option<CrateType>) -> bool {
+ if !self.target.crt_static_respected {
+ // If the target does not opt in to crt-static support, use its default.
+ return self.target.crt_static_default;
+ }
+
+ let requested_features = self.opts.cg.target_feature.split(',');
+ let found_negative = requested_features.clone().any(|r| r == "-crt-static");
+ let found_positive = requested_features.clone().any(|r| r == "+crt-static");
+
+ // JUSTIFICATION: necessary use of crate_types directly (see FIXME below)
+ #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+ if found_positive || found_negative {
+ found_positive
+ } else if crate_type == Some(CrateType::ProcMacro)
+ || crate_type == None && self.opts.crate_types.contains(&CrateType::ProcMacro)
+ {
+ // FIXME: When crate_type is not available,
+ // we use compiler options to determine the crate_type.
+ // We can't check `#![crate_type = "proc-macro"]` here.
+ false
+ } else {
+ self.target.crt_static_default
+ }
+ }
+
+ pub fn is_wasi_reactor(&self) -> bool {
+ self.target.options.os == "wasi"
+ && matches!(
+ self.opts.unstable_opts.wasi_exec_model,
+ Some(config::WasiExecModel::Reactor)
+ )
+ }
+
+ pub fn target_can_use_split_dwarf(&self) -> bool {
+ !self.target.is_like_windows && !self.target.is_like_osx
+ }
+
+ pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String {
+ format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64())
+ }
+
+ pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> {
+ filesearch::FileSearch::new(
+ &self.sysroot,
+ self.opts.target_triple.triple(),
+ &self.opts.search_paths,
+ &self.target_tlib_path,
+ kind,
+ )
+ }
+ pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> {
+ filesearch::FileSearch::new(
+ &self.sysroot,
+ config::host_triple(),
+ &self.opts.search_paths,
+ &self.host_tlib_path,
+ kind,
+ )
+ }
+
+ /// Returns a list of directories where target-specific tool binaries are located.
+ pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec<PathBuf> {
+ let rustlib_path = rustc_target::target_rustlib_path(&self.sysroot, &config::host_triple());
+ let p = PathBuf::from_iter([
+ Path::new(&self.sysroot),
+ Path::new(&rustlib_path),
+ Path::new("bin"),
+ ]);
+ if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] }
+ }
+
+ pub fn init_incr_comp_session(
+ &self,
+ session_dir: PathBuf,
+ lock_file: flock::Lock,
+ load_dep_graph: bool,
+ ) {
+ let mut incr_comp_session = self.incr_comp_session.borrow_mut();
+
+ if let IncrCompSession::NotInitialized = *incr_comp_session {
+ } else {
+ panic!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session)
+ }
+
+ *incr_comp_session =
+ IncrCompSession::Active { session_directory: session_dir, lock_file, load_dep_graph };
+ }
+
+ pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) {
+ let mut incr_comp_session = self.incr_comp_session.borrow_mut();
+
+ if let IncrCompSession::Active { .. } = *incr_comp_session {
+ } else {
+ panic!("trying to finalize `IncrCompSession` `{:?}`", *incr_comp_session);
+ }
+
+ // Note: this will also drop the lock file, thus unlocking the directory.
+ *incr_comp_session = IncrCompSession::Finalized { session_directory: new_directory_path };
+ }
+
+ pub fn mark_incr_comp_session_as_invalid(&self) {
+ let mut incr_comp_session = self.incr_comp_session.borrow_mut();
+
+ let session_directory = match *incr_comp_session {
+ IncrCompSession::Active { ref session_directory, .. } => session_directory.clone(),
+ IncrCompSession::InvalidBecauseOfErrors { .. } => return,
+ _ => panic!("trying to invalidate `IncrCompSession` `{:?}`", *incr_comp_session),
+ };
+
+ // Note: this will also drop the lock file, thus unlocking the directory.
+ *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { session_directory };
+ }
+
+ pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> {
+ let incr_comp_session = self.incr_comp_session.borrow();
+ cell::Ref::map(incr_comp_session, |incr_comp_session| match *incr_comp_session {
+ IncrCompSession::NotInitialized => panic!(
+ "trying to get session directory from `IncrCompSession`: {:?}",
+ *incr_comp_session,
+ ),
+ IncrCompSession::Active { ref session_directory, .. }
+ | IncrCompSession::Finalized { ref session_directory }
+ | IncrCompSession::InvalidBecauseOfErrors { ref session_directory } => {
+ session_directory
+ }
+ })
+ }
+
+ pub fn incr_comp_session_dir_opt(&self) -> Option<cell::Ref<'_, PathBuf>> {
+ self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir())
+ }
+
+ pub fn print_perf_stats(&self) {
+ eprintln!(
+ "Total time spent computing symbol hashes: {}",
+ duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock())
+ );
+ eprintln!(
+ "Total queries canonicalized: {}",
+ self.perf_stats.queries_canonicalized.load(Ordering::Relaxed)
+ );
+ eprintln!(
+ "normalize_generic_arg_after_erasing_regions: {}",
+ self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed)
+ );
+ eprintln!(
+ "normalize_projection_ty: {}",
+ self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed)
+ );
+ }
+
+ /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n.
+ /// This expends fuel if applicable, and records fuel if applicable.
+ pub fn consider_optimizing<T: Fn() -> String>(&self, crate_name: &str, msg: T) -> bool {
+ let mut ret = true;
+ if let Some((ref c, _)) = self.opts.unstable_opts.fuel {
+ if c == crate_name {
+ assert_eq!(self.threads(), 1);
+ let mut fuel = self.optimization_fuel.lock();
+ ret = fuel.remaining != 0;
+ if fuel.remaining == 0 && !fuel.out_of_fuel {
+ if self.diagnostic().can_emit_warnings() {
+ // We only call `msg` in case we can actually emit warnings.
+ // Otherwise, this could cause a `delay_good_path_bug` to
+ // trigger (issue #79546).
+ self.warn(&format!("optimization-fuel-exhausted: {}", msg()));
+ }
+ fuel.out_of_fuel = true;
+ } else if fuel.remaining > 0 {
+ fuel.remaining -= 1;
+ }
+ }
+ }
+ if let Some(ref c) = self.opts.unstable_opts.print_fuel {
+ if c == crate_name {
+ assert_eq!(self.threads(), 1);
+ self.print_fuel.fetch_add(1, SeqCst);
+ }
+ }
+ ret
+ }
+
+ pub fn rust_2015(&self) -> bool {
+ self.edition() == Edition::Edition2015
+ }
+
+ /// Are we allowed to use features from the Rust 2018 edition?
+ pub fn rust_2018(&self) -> bool {
+ self.edition() >= Edition::Edition2018
+ }
+
+ /// Are we allowed to use features from the Rust 2021 edition?
+ pub fn rust_2021(&self) -> bool {
+ self.edition() >= Edition::Edition2021
+ }
+
+ /// Are we allowed to use features from the Rust 2024 edition?
+ pub fn rust_2024(&self) -> bool {
+ self.edition() >= Edition::Edition2024
+ }
+
+ /// Returns `true` if we cannot skip the PLT for shared library calls.
+ pub fn needs_plt(&self) -> bool {
+ // Check if the current target usually needs PLT to be enabled.
+ // The user can use the command line flag to override it.
+ let needs_plt = self.target.needs_plt;
+
+ let dbg_opts = &self.opts.unstable_opts;
+
+ let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level);
+
+ // Only enable this optimization by default if full relro is also enabled.
+ // In this case, lazy binding was already unavailable, so nothing is lost.
+ // This also ensures `-Wl,-z,now` is supported by the linker.
+ let full_relro = RelroLevel::Full == relro_level;
+
+ // If user didn't explicitly forced us to use / skip the PLT,
+ // then try to skip it where possible.
+ dbg_opts.plt.unwrap_or(needs_plt || !full_relro)
+ }
+
+ /// Checks if LLVM lifetime markers should be emitted.
+ pub fn emit_lifetime_markers(&self) -> bool {
+ self.opts.optimize != config::OptLevel::No
+ // AddressSanitizer uses lifetimes to detect use after scope bugs.
+ // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
+ // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
+ || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
+ }
+
+ pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {
+ [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
+ .iter()
+ .any(|kind| attr.has_name(*kind))
+ }
+
+ pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool {
+ attrs.iter().any(|item| item.has_name(name))
+ }
+
+ pub fn find_by_name<'a>(
+ &'a self,
+ attrs: &'a [Attribute],
+ name: Symbol,
+ ) -> Option<&'a Attribute> {
+ attrs.iter().find(|attr| attr.has_name(name))
+ }
+
+ pub fn filter_by_name<'a>(
+ &'a self,
+ attrs: &'a [Attribute],
+ name: Symbol,
+ ) -> impl Iterator<Item = &'a Attribute> {
+ attrs.iter().filter(move |attr| attr.has_name(name))
+ }
+
+ pub fn first_attr_value_str_by_name(
+ &self,
+ attrs: &[Attribute],
+ name: Symbol,
+ ) -> Option<Symbol> {
+ attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str())
+ }
+}
+
+// JUSTIFICATION: defn of the suggested wrapper fns
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+impl Session {
+ pub fn verbose(&self) -> bool {
+ self.opts.unstable_opts.verbose
+ }
+
+ pub fn instrument_mcount(&self) -> bool {
+ self.opts.unstable_opts.instrument_mcount
+ }
+
+ pub fn time_llvm_passes(&self) -> bool {
+ self.opts.unstable_opts.time_llvm_passes
+ }
+
+ pub fn meta_stats(&self) -> bool {
+ self.opts.unstable_opts.meta_stats
+ }
+
+ pub fn asm_comments(&self) -> bool {
+ self.opts.unstable_opts.asm_comments
+ }
+
+ pub fn verify_llvm_ir(&self) -> bool {
+ self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some()
+ }
+
+ pub fn print_llvm_passes(&self) -> bool {
+ self.opts.unstable_opts.print_llvm_passes
+ }
+
+ pub fn binary_dep_depinfo(&self) -> bool {
+ self.opts.unstable_opts.binary_dep_depinfo
+ }
+
+ pub fn mir_opt_level(&self) -> usize {
+ self.opts
+ .unstable_opts
+ .mir_opt_level
+ .unwrap_or_else(|| if self.opts.optimize != OptLevel::No { 2 } else { 1 })
+ }
+
+ /// Calculates the flavor of LTO to use for this compilation.
+ pub fn lto(&self) -> config::Lto {
+ // If our target has codegen requirements ignore the command line
+ if self.target.requires_lto {
+ return config::Lto::Fat;
+ }
+
+ // If the user specified something, return that. If they only said `-C
+ // lto` and we've for whatever reason forced off ThinLTO via the CLI,
+ // then ensure we can't use a ThinLTO.
+ match self.opts.cg.lto {
+ config::LtoCli::Unspecified => {
+ // The compiler was invoked without the `-Clto` flag. Fall
+ // through to the default handling
+ }
+ config::LtoCli::No => {
+ // The user explicitly opted out of any kind of LTO
+ return config::Lto::No;
+ }
+ config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => {
+ // All of these mean fat LTO
+ return config::Lto::Fat;
+ }
+ config::LtoCli::Thin => {
+ return if self.opts.cli_forced_thinlto_off {
+ config::Lto::Fat
+ } else {
+ config::Lto::Thin
+ };
+ }
+ }
+
+ // Ok at this point the target doesn't require anything and the user
+ // hasn't asked for anything. Our next decision is whether or not
+ // we enable "auto" ThinLTO where we use multiple codegen units and
+ // then do ThinLTO over those codegen units. The logic below will
+ // either return `No` or `ThinLocal`.
+
+ // If processing command line options determined that we're incompatible
+ // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option.
+ if self.opts.cli_forced_thinlto_off {
+ return config::Lto::No;
+ }
+
+ // If `-Z thinlto` specified process that, but note that this is mostly
+ // a deprecated option now that `-C lto=thin` exists.
+ if let Some(enabled) = self.opts.unstable_opts.thinlto {
+ if enabled {
+ return config::Lto::ThinLocal;
+ } else {
+ return config::Lto::No;
+ }
+ }
+
+ // If there's only one codegen unit and LTO isn't enabled then there's
+ // no need for ThinLTO so just return false.
+ if self.codegen_units() == 1 {
+ return config::Lto::No;
+ }
+
+ // Now we're in "defaults" territory. By default we enable ThinLTO for
+ // optimized compiles (anything greater than O0).
+ match self.opts.optimize {
+ config::OptLevel::No => config::Lto::No,
+ _ => config::Lto::ThinLocal,
+ }
+ }
+
+ /// Returns the panic strategy for this compile session. If the user explicitly selected one
+ /// using '-C panic', use that, otherwise use the panic strategy defined by the target.
+ pub fn panic_strategy(&self) -> PanicStrategy {
+ self.opts.cg.panic.unwrap_or(self.target.panic_strategy)
+ }
+
+ pub fn fewer_names(&self) -> bool {
+ if let Some(fewer_names) = self.opts.unstable_opts.fewer_names {
+ fewer_names
+ } else {
+ let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly)
+ || self.opts.output_types.contains_key(&OutputType::Bitcode)
+ // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue.
+ || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY);
+ !more_names
+ }
+ }
+
+ pub fn unstable_options(&self) -> bool {
+ self.opts.unstable_opts.unstable_options
+ }
+
+ pub fn is_nightly_build(&self) -> bool {
+ self.opts.unstable_features.is_nightly_build()
+ }
+
+ pub fn overflow_checks(&self) -> bool {
+ self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions)
+ }
+
+ pub fn relocation_model(&self) -> RelocModel {
+ self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
+ }
+
+ pub fn code_model(&self) -> Option<CodeModel> {
+ self.opts.cg.code_model.or(self.target.code_model)
+ }
+
+ pub fn tls_model(&self) -> TlsModel {
+ self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model)
+ }
+
+ pub fn split_debuginfo(&self) -> SplitDebuginfo {
+ self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
+ }
+
+ pub fn stack_protector(&self) -> StackProtector {
+ if self.target.options.supports_stack_protector {
+ self.opts.unstable_opts.stack_protector
+ } else {
+ StackProtector::None
+ }
+ }
+
+ pub fn must_emit_unwind_tables(&self) -> bool {
+ // This is used to control the emission of the `uwtable` attribute on
+ // LLVM functions.
+ //
+ // Unwind tables are needed when compiling with `-C panic=unwind`, but
+ // LLVM won't omit unwind tables unless the function is also marked as
+ // `nounwind`, so users are allowed to disable `uwtable` emission.
+ // Historically rustc always emits `uwtable` attributes by default, so
+ // even they can be disabled, they're still emitted by default.
+ //
+ // On some targets (including windows), however, exceptions include
+ // other events such as illegal instructions, segfaults, etc. This means
+ // that on Windows we end up still needing unwind tables even if the `-C
+ // panic=abort` flag is passed.
+ //
+ // You can also find more info on why Windows needs unwind tables in:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
+ //
+ // If a target requires unwind tables, then they must be emitted.
+ // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>`
+ // value, if it is provided, or disable them, if not.
+ self.target.requires_uwtable
+ || self.opts.cg.force_unwind_tables.unwrap_or(
+ self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable,
+ )
+ }
+
+ /// Returns the number of query threads that should be used for this
+ /// compilation
+ pub fn threads(&self) -> usize {
+ self.opts.unstable_opts.threads
+ }
+
+ /// Returns the number of codegen units that should be used for this
+ /// compilation
+ pub fn codegen_units(&self) -> usize {
+ if let Some(n) = self.opts.cli_forced_codegen_units {
+ return n;
+ }
+ if let Some(n) = self.target.default_codegen_units {
+ return n as usize;
+ }
+
+ // If incremental compilation is turned on, we default to a high number
+ // codegen units in order to reduce the "collateral damage" small
+ // changes cause.
+ if self.opts.incremental.is_some() {
+ return 256;
+ }
+
+ // Why is 16 codegen units the default all the time?
+ //
+ // The main reason for enabling multiple codegen units by default is to
+ // leverage the ability for the codegen backend to do codegen and
+ // optimization in parallel. This allows us, especially for large crates, to
+ // make good use of all available resources on the machine once we've
+ // hit that stage of compilation. Large crates especially then often
+ // take a long time in codegen/optimization and this helps us amortize that
+ // cost.
+ //
+ // Note that a high number here doesn't mean that we'll be spawning a
+ // large number of threads in parallel. The backend of rustc contains
+ // global rate limiting through the `jobserver` crate so we'll never
+ // overload the system with too much work, but rather we'll only be
+ // optimizing when we're otherwise cooperating with other instances of
+ // rustc.
+ //
+ // Rather a high number here means that we should be able to keep a lot
+ // of idle cpus busy. By ensuring that no codegen unit takes *too* long
+ // to build we'll be guaranteed that all cpus will finish pretty closely
+ // to one another and we should make relatively optimal use of system
+ // resources
+ //
+ // Note that the main cost of codegen units is that it prevents LLVM
+ // from inlining across codegen units. Users in general don't have a lot
+ // of control over how codegen units are split up so it's our job in the
+ // compiler to ensure that undue performance isn't lost when using
+ // codegen units (aka we can't require everyone to slap `#[inline]` on
+ // everything).
+ //
+ // If we're compiling at `-O0` then the number doesn't really matter too
+ // much because performance doesn't matter and inlining is ok to lose.
+ // In debug mode we just want to try to guarantee that no cpu is stuck
+ // doing work that could otherwise be farmed to others.
+ //
+ // In release mode, however (O1 and above) performance does indeed
+ // matter! To recover the loss in performance due to inlining we'll be
+ // enabling ThinLTO by default (the function for which is just below).
+ // This will ensure that we recover any inlining wins we otherwise lost
+ // through codegen unit partitioning.
+ //
+ // ---
+ //
+ // Ok that's a lot of words but the basic tl;dr; is that we want a high
+ // number here -- but not too high. Additionally we're "safe" to have it
+ // always at the same number at all optimization levels.
+ //
+ // As a result 16 was chosen here! Mostly because it was a power of 2
+ // and most benchmarks agreed it was roughly a local optimum. Not very
+ // scientific.
+ 16
+ }
+
+ pub fn teach(&self, code: &DiagnosticId) -> bool {
+ self.opts.unstable_opts.teach && self.diagnostic().must_teach(code)
+ }
+
+ pub fn edition(&self) -> Edition {
+ self.opts.edition
+ }
+
+ pub fn link_dead_code(&self) -> bool {
+ self.opts.cg.link_dead_code.unwrap_or(false)
+ }
+}
+
+// JUSTIFICATION: part of session construction
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+fn default_emitter(
+ sopts: &config::Options,
+ registry: rustc_errors::registry::Registry,
+ source_map: Lrc<SourceMap>,
+ bundle: Option<Lrc<FluentBundle>>,
+ fallback_bundle: LazyFallbackBundle,
+ emitter_dest: Option<Box<dyn Write + Send>>,
+) -> Box<dyn Emitter + sync::Send> {
+ let macro_backtrace = sopts.unstable_opts.macro_backtrace;
+ match (sopts.error_format, emitter_dest) {
+ (config::ErrorOutputType::HumanReadable(kind), dst) => {
+ let (short, color_config) = kind.unzip();
+
+ if let HumanReadableErrorType::AnnotateSnippet(_) = kind {
+ let emitter = AnnotateSnippetEmitterWriter::new(
+ Some(source_map),
+ bundle,
+ fallback_bundle,
+ short,
+ macro_backtrace,
+ );
+ Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
+ } else {
+ let emitter = match dst {
+ None => EmitterWriter::stderr(
+ color_config,
+ Some(source_map),
+ bundle,
+ fallback_bundle,
+ short,
+ sopts.unstable_opts.teach,
+ sopts.diagnostic_width,
+ macro_backtrace,
+ ),
+ Some(dst) => EmitterWriter::new(
+ dst,
+ Some(source_map),
+ bundle,
+ fallback_bundle,
+ short,
+ false, // no teach messages when writing to a buffer
+ false, // no colors when writing to a buffer
+ None, // no diagnostic width
+ macro_backtrace,
+ ),
+ };
+ Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
+ }
+ }
+ (config::ErrorOutputType::Json { pretty, json_rendered }, None) => Box::new(
+ JsonEmitter::stderr(
+ Some(registry),
+ source_map,
+ bundle,
+ fallback_bundle,
+ pretty,
+ json_rendered,
+ sopts.diagnostic_width,
+ macro_backtrace,
+ )
+ .ui_testing(sopts.unstable_opts.ui_testing),
+ ),
+ (config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new(
+ JsonEmitter::new(
+ dst,
+ Some(registry),
+ source_map,
+ bundle,
+ fallback_bundle,
+ pretty,
+ json_rendered,
+ sopts.diagnostic_width,
+ macro_backtrace,
+ )
+ .ui_testing(sopts.unstable_opts.ui_testing),
+ ),
+ }
+}
+
+pub enum DiagnosticOutput {
+ Default,
+ Raw(Box<dyn Write + Send>),
+}
+
+// JUSTIFICATION: literally session construction
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+pub fn build_session(
+ sopts: config::Options,
+ local_crate_source_file: Option<PathBuf>,
+ bundle: Option<Lrc<rustc_errors::FluentBundle>>,
+ registry: rustc_errors::registry::Registry,
+ diagnostics_output: DiagnosticOutput,
+ driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
+ file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
+ target_override: Option<Target>,
+) -> Session {
+ // FIXME: This is not general enough to make the warning lint completely override
+ // normal diagnostic warnings, since the warning lint can also be denied and changed
+ // later via the source code.
+ let warnings_allow = sopts
+ .lint_opts
+ .iter()
+ .filter(|&&(ref key, _)| *key == "warnings")
+ .map(|&(_, ref level)| *level == lint::Allow)
+ .last()
+ .unwrap_or(false);
+ let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow);
+ let can_emit_warnings = !(warnings_allow || cap_lints_allow);
+
+ let write_dest = match diagnostics_output {
+ DiagnosticOutput::Default => None,
+ DiagnosticOutput::Raw(write) => Some(write),
+ };
+
+ let sysroot = match &sopts.maybe_sysroot {
+ Some(sysroot) => sysroot.clone(),
+ None => filesearch::get_or_default_sysroot(),
+ };
+
+ let target_cfg = config::build_target_config(&sopts, target_override, &sysroot);
+ let host_triple = TargetTriple::from_triple(config::host_triple());
+ let (host, target_warnings) = Target::search(&host_triple, &sysroot).unwrap_or_else(|e| {
+ early_error(sopts.error_format, &format!("Error loading host specification: {e}"))
+ });
+ for warning in target_warnings.warning_messages() {
+ early_warn(sopts.error_format, &warning)
+ }
+
+ let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader));
+ let hash_kind = sopts.unstable_opts.src_hash_algorithm.unwrap_or_else(|| {
+ if target_cfg.is_like_msvc {
+ SourceFileHashAlgorithm::Sha1
+ } else {
+ SourceFileHashAlgorithm::Md5
+ }
+ });
+ let source_map = Lrc::new(SourceMap::with_file_loader_and_hash_kind(
+ loader,
+ sopts.file_path_mapping(),
+ hash_kind,
+ ));
+
+ let fallback_bundle = fallback_fluent_bundle(
+ rustc_errors::DEFAULT_LOCALE_RESOURCES,
+ sopts.unstable_opts.translate_directionality_markers,
+ );
+ let emitter =
+ default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle, write_dest);
+
+ let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags(
+ emitter,
+ sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings),
+ );
+
+ let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.unstable_opts.self_profile
+ {
+ let directory =
+ if let Some(ref directory) = d { directory } else { std::path::Path::new(".") };
+
+ let profiler = SelfProfiler::new(
+ directory,
+ sopts.crate_name.as_deref(),
+ sopts.unstable_opts.self_profile_events.as_ref().map(|xs| &xs[..]),
+ &sopts.unstable_opts.self_profile_counter,
+ );
+ match profiler {
+ Ok(profiler) => Some(Arc::new(profiler)),
+ Err(e) => {
+ early_warn(sopts.error_format, &format!("failed to create profiler: {e}"));
+ None
+ }
+ }
+ } else {
+ None
+ };
+
+ let mut parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map);
+ parse_sess.assume_incomplete_release = sopts.unstable_opts.assume_incomplete_release;
+
+ let host_triple = config::host_triple();
+ let target_triple = sopts.target_triple.triple();
+ let host_tlib_path = Lrc::new(SearchPath::from_sysroot_and_triple(&sysroot, host_triple));
+ let target_tlib_path = if host_triple == target_triple {
+ // Use the same `SearchPath` if host and target triple are identical to avoid unnecessary
+ // rescanning of the target lib path and an unnecessary allocation.
+ host_tlib_path.clone()
+ } else {
+ Lrc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple))
+ };
+
+ let file_path_mapping = sopts.file_path_mapping();
+
+ let local_crate_source_file =
+ local_crate_source_file.map(|path| file_path_mapping.map_prefix(path).0);
+
+ let optimization_fuel = Lock::new(OptimizationFuel {
+ remaining: sopts.unstable_opts.fuel.as_ref().map_or(0, |i| i.1),
+ out_of_fuel: false,
+ });
+ let print_fuel = AtomicU64::new(0);
+
+ let cgu_reuse_tracker = if sopts.unstable_opts.query_dep_graph {
+ CguReuseTracker::new()
+ } else {
+ CguReuseTracker::new_disabled()
+ };
+
+ let prof =
+ SelfProfilerRef::new(self_profiler, sopts.time_passes(), sopts.unstable_opts.time_passes);
+
+ let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") {
+ Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate,
+ Ok(ref val) if val != "0" => CtfeBacktrace::Capture,
+ _ => CtfeBacktrace::Disabled,
+ });
+
+ let asm_arch =
+ if target_cfg.allow_asm { InlineAsmArch::from_str(&target_cfg.arch).ok() } else { None };
+
+ let sess = Session {
+ target: target_cfg,
+ host,
+ opts: sopts,
+ host_tlib_path,
+ target_tlib_path,
+ parse_sess,
+ sysroot,
+ local_crate_source_file,
+ crate_types: OnceCell::new(),
+ stable_crate_id: OnceCell::new(),
+ features: OnceCell::new(),
+ incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
+ cgu_reuse_tracker,
+ prof,
+ perf_stats: PerfStats {
+ symbol_hash_time: Lock::new(Duration::from_secs(0)),
+ queries_canonicalized: AtomicUsize::new(0),
+ normalize_generic_arg_after_erasing_regions: AtomicUsize::new(0),
+ normalize_projection_ty: AtomicUsize::new(0),
+ },
+ code_stats: Default::default(),
+ optimization_fuel,
+ print_fuel,
+ jobserver: jobserver::client(),
+ driver_lint_caps,
+ ctfe_backtrace,
+ miri_unleashed_features: Lock::new(Default::default()),
+ asm_arch,
+ target_features: FxHashSet::default(),
+ unstable_target_features: FxHashSet::default(),
+ };
+
+ validate_commandline_args_with_session_available(&sess);
+
+ sess
+}
+
+/// Validate command line arguments with a `Session`.
+///
+/// If it is useful to have a Session available already for validating a commandline argument, you
+/// can do so here.
+// JUSTIFICATION: needs to access args to validate them
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+fn validate_commandline_args_with_session_available(sess: &Session) {
+ // Since we don't know if code in an rlib will be linked to statically or
+ // dynamically downstream, rustc generates `__imp_` symbols that help linkers
+ // on Windows deal with this lack of knowledge (#27438). Unfortunately,
+ // these manually generated symbols confuse LLD when it tries to merge
+ // bitcode during ThinLTO. Therefore we disallow dynamic linking on Windows
+ // when compiling for LLD ThinLTO. This way we can validly just not generate
+ // the `dllimport` attributes and `__imp_` symbols in that case.
+ if sess.opts.cg.linker_plugin_lto.enabled()
+ && sess.opts.cg.prefer_dynamic
+ && sess.target.is_like_windows
+ {
+ sess.err(
+ "Linker plugin based LTO is not supported together with \
+ `-C prefer-dynamic` when targeting Windows-like targets",
+ );
+ }
+
+ // Make sure that any given profiling data actually exists so LLVM can't
+ // decide to silently skip PGO.
+ if let Some(ref path) = sess.opts.cg.profile_use {
+ if !path.exists() {
+ sess.err(&format!(
+ "File `{}` passed to `-C profile-use` does not exist.",
+ path.display()
+ ));
+ }
+ }
+
+ // Do the same for sample profile data.
+ if let Some(ref path) = sess.opts.unstable_opts.profile_sample_use {
+ if !path.exists() {
+ sess.err(&format!(
+ "File `{}` passed to `-C profile-sample-use` does not exist.",
+ path.display()
+ ));
+ }
+ }
+
+ // Unwind tables cannot be disabled if the target requires them.
+ if let Some(include_uwtables) = sess.opts.cg.force_unwind_tables {
+ if sess.target.requires_uwtable && !include_uwtables {
+ sess.err(
+ "target requires unwind tables, they cannot be disabled with \
+ `-C force-unwind-tables=no`.",
+ );
+ }
+ }
+
+ // Sanitizers can only be used on platforms that we know have working sanitizer codegen.
+ let supported_sanitizers = sess.target.options.supported_sanitizers;
+ let unsupported_sanitizers = sess.opts.unstable_opts.sanitizer - supported_sanitizers;
+ match unsupported_sanitizers.into_iter().count() {
+ 0 => {}
+ 1 => {
+ sess.err(&format!(
+ "{} sanitizer is not supported for this target",
+ unsupported_sanitizers
+ ));
+ }
+ _ => {
+ sess.err(&format!(
+ "{} sanitizers are not supported for this target",
+ unsupported_sanitizers
+ ));
+ }
+ }
+ // Cannot mix and match sanitizers.
+ let mut sanitizer_iter = sess.opts.unstable_opts.sanitizer.into_iter();
+ if let (Some(first), Some(second)) = (sanitizer_iter.next(), sanitizer_iter.next()) {
+ sess.err(&format!("`-Zsanitizer={first}` is incompatible with `-Zsanitizer={second}`"));
+ }
+
+ // Cannot enable crt-static with sanitizers on Linux
+ if sess.crt_static(None) && !sess.opts.unstable_opts.sanitizer.is_empty() {
+ sess.err(
+ "sanitizer is incompatible with statically linked libc, \
+ disable it using `-C target-feature=-crt-static`",
+ );
+ }
+
+ // LLVM CFI and VFE both require LTO.
+ if sess.lto() != config::Lto::Fat {
+ if sess.is_sanitizer_cfi_enabled() {
+ sess.err("`-Zsanitizer=cfi` requires `-Clto`");
+ }
+ if sess.opts.unstable_opts.virtual_function_elimination {
+ sess.err("`-Zvirtual-function-elimination` requires `-Clto`");
+ }
+ }
+
+ if sess.opts.unstable_opts.stack_protector != StackProtector::None {
+ if !sess.target.options.supports_stack_protector {
+ sess.warn(&format!(
+ "`-Z stack-protector={}` is not supported for target {} and will be ignored",
+ sess.opts.unstable_opts.stack_protector, sess.opts.target_triple
+ ))
+ }
+ }
+
+ if let Some(dwarf_version) = sess.opts.unstable_opts.dwarf_version {
+ if dwarf_version > 5 {
+ sess.err(&format!("requested DWARF version {} is greater than 5", dwarf_version));
+ }
+ }
+}
+
+/// Holds data on the current incremental compilation session, if there is one.
+#[derive(Debug)]
+pub enum IncrCompSession {
+ /// This is the state the session will be in until the incr. comp. dir is
+ /// needed.
+ NotInitialized,
+ /// This is the state during which the session directory is private and can
+ /// be modified.
+ Active { session_directory: PathBuf, lock_file: flock::Lock, load_dep_graph: bool },
+ /// This is the state after the session directory has been finalized. In this
+ /// state, the contents of the directory must not be modified any more.
+ Finalized { session_directory: PathBuf },
+ /// This is an error state that is reached when some compilation error has
+ /// occurred. It indicates that the contents of the session directory must
+ /// not be used, since they might be invalid.
+ InvalidBecauseOfErrors { session_directory: PathBuf },
+}
+
+fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler {
+ let fallback_bundle = fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let emitter: Box<dyn Emitter + sync::Send> = match output {
+ config::ErrorOutputType::HumanReadable(kind) => {
+ let (short, color_config) = kind.unzip();
+ Box::new(EmitterWriter::stderr(
+ color_config,
+ None,
+ None,
+ fallback_bundle,
+ short,
+ false,
+ None,
+ false,
+ ))
+ }
+ config::ErrorOutputType::Json { pretty, json_rendered } => {
+ Box::new(JsonEmitter::basic(pretty, json_rendered, None, fallback_bundle, None, false))
+ }
+ };
+ rustc_errors::Handler::with_emitter(true, None, emitter)
+}
+
+pub fn early_error_no_abort(output: config::ErrorOutputType, msg: &str) -> ErrorGuaranteed {
+ early_error_handler(output).struct_err(msg).emit()
+}
+
+pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
+ early_error_handler(output).struct_fatal(msg).emit()
+}
+
+pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
+ early_error_handler(output).struct_warn(msg).emit()
+}
diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs
new file mode 100644
index 000000000..9a4f6f9f9
--- /dev/null
+++ b/compiler/rustc_session/src/utils.rs
@@ -0,0 +1,93 @@
+use crate::session::Session;
+use rustc_data_structures::profiling::VerboseTimingGuard;
+use std::path::{Path, PathBuf};
+
+impl Session {
+ pub fn timer<'a>(&'a self, what: &'static str) -> VerboseTimingGuard<'a> {
+ self.prof.verbose_generic_activity(what)
+ }
+ pub fn time<R>(&self, what: &'static str, f: impl FnOnce() -> R) -> R {
+ self.prof.verbose_generic_activity(what).run(f)
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
+#[derive(HashStable_Generic)]
+pub enum NativeLibKind {
+ /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC)
+ Static {
+ /// Whether to bundle objects from static library into produced rlib
+ bundle: Option<bool>,
+ /// Whether to link static library without throwing any object files away
+ whole_archive: Option<bool>,
+ },
+ /// Dynamic library (e.g. `libfoo.so` on Linux)
+ /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC).
+ Dylib {
+ /// Whether the dynamic library will be linked only if it satisfies some undefined symbols
+ as_needed: Option<bool>,
+ },
+ /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library.
+ RawDylib,
+ /// A macOS-specific kind of dynamic libraries.
+ Framework {
+ /// Whether the framework will be linked only if it satisfies some undefined symbols
+ as_needed: Option<bool>,
+ },
+ /// Argument which is passed to linker, relative order with libraries and other arguments
+ /// is preserved
+ LinkArg,
+ /// The library kind wasn't specified, `Dylib` is currently used as a default.
+ Unspecified,
+}
+
+impl NativeLibKind {
+ pub fn has_modifiers(&self) -> bool {
+ match self {
+ NativeLibKind::Static { bundle, whole_archive } => {
+ bundle.is_some() || whole_archive.is_some()
+ }
+ NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } => {
+ as_needed.is_some()
+ }
+ NativeLibKind::RawDylib | NativeLibKind::Unspecified | NativeLibKind::LinkArg => false,
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
+#[derive(HashStable_Generic)]
+pub struct NativeLib {
+ pub name: String,
+ pub new_name: Option<String>,
+ pub kind: NativeLibKind,
+ pub verbatim: Option<bool>,
+}
+
+impl NativeLib {
+ pub fn has_modifiers(&self) -> bool {
+ self.verbatim.is_some() || self.kind.has_modifiers()
+ }
+}
+
+/// A path that has been canonicalized along with its original, non-canonicalized form
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct CanonicalizedPath {
+ // Optional since canonicalization can sometimes fail
+ canonicalized: Option<PathBuf>,
+ original: PathBuf,
+}
+
+impl CanonicalizedPath {
+ pub fn new(path: &Path) -> Self {
+ Self { original: path.to_owned(), canonicalized: std::fs::canonicalize(path).ok() }
+ }
+
+ pub fn canonicalized(&self) -> &PathBuf {
+ self.canonicalized.as_ref().unwrap_or(self.original())
+ }
+
+ pub fn original(&self) -> &PathBuf {
+ &self.original
+ }
+}