use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use alloc::collections::btree_map::{
Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter,
};
use alloc::collections::{BTreeMap, BTreeSet};
use core::hash::Hash;
use core::num::NonZero;
use eko::path::{Path, PathBuf};
use core::str::{self, FromStr};
use core::{iter};
use crate::rustc_data_structures::fx::FxIndexMap;
use crate::rustc_data_structures::stable_hash::{StableHasher, StableOrd};
use crate::rustc_errors::emitter::HumanReadableErrorType;
use crate::rustc_errors::{ColorConfig, DiagCtxtFlags};
use crate::rustc_feature::UnstableFeatures;
use crate::rustc_hashes::Hash64;
use rustc_macros::{BlobDecodable, Decodable, Encodable, StableHash};
use crate::rustc_span::edition::DEFAULT_EDITION;
use crate::rustc_span::source_map::FilePathMapping;
use crate::rustc_span::{
FileName, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, Symbol, sym,
};
use crate::rustc_structures::CrateType;
use crate::rustc_target::spec::{
LinkSelfContainedComponents, LinkerFeatures, SplitDebuginfo, Target, TargetTuple,
};
use tracing::debug;
pub use crate::rustc_session::config::cfg::{Cfg, CheckCfg, ExpectedValues};
use crate::rustc_session::diagnostics::FileWriteFail;
pub use crate::rustc_session::options::*;
use crate::rustc_session::utils::CanonicalizedPath;
use crate::rustc_session::{EarlyDiagCtxt, Session, filesearch};
mod cfg;
pub mod sigpipe;
pub const NATIVE_CPU: &str = "native";
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum Strip {
None,
Debuginfo,
Symbols,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum CFGuard {
Disabled,
NoChecks,
Checks,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum CFProtection {
None,
Branch,
Return,
Full,
}
#[derive(Clone, Copy, Debug, PartialEq, Hash, StableHash, Encodable, Decodable)]
pub enum OptLevel {
No,
Less,
More,
Aggressive,
Size,
SizeMin,
}
#[derive(Clone, PartialEq, Encodable, Decodable)]
pub enum Lto {
No,
Thin,
ThinLocal,
Fat,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum LtoCli {
No,
Yes,
NoParam,
Thin,
Fat,
Unspecified,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum InstrumentCoverage {
No,
Yes,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct CoverageOptions {
pub level: CoverageLevel,
pub discard_all_spans_in_codegen: bool,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub enum CoverageLevel {
#[default]
Block,
Branch,
Condition,
}
#[derive(Clone, PartialEq, Hash, Debug, Encodable, Decodable)]
pub enum Offload {
Device(String),
Host(String),
Test,
HostMetadata(String),
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Hash, Encodable, Decodable)]
pub struct CodegenRetagOptions {
pub no_precise_im: bool,
pub no_precise_pin: bool,
}
#[derive(Clone, PartialEq, Hash, Debug, Encodable, Decodable)]
pub enum AutoDiff {
Enable,
PrintTA,
PrintTAFn(String),
PrintAA,
PrintPerf,
PrintSteps,
PrintModBefore,
PrintModAfter,
PrintModFinal,
PrintPasses,
NoPostopt,
LooseTypes,
Inline,
NoTT,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum AnnotateMoves {
Disabled,
Enabled(Option<u64>),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct InstrumentMcountOpts {
pub no_call: bool,
pub record: bool,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum InstrumentMcount {
Disabled,
Mcount(InstrumentMcountOpts),
Fentry(InstrumentMcountOpts),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct InstrumentXRay {
pub always: bool,
pub never: bool,
pub ignore_loops: bool,
pub instruction_threshold: Option<usize>,
pub skip_entry: bool,
pub skip_exit: bool,
}
#[derive(Clone, PartialEq, Hash, Debug)]
pub enum LinkerPluginLto {
LinkerPlugin(PathBuf),
LinkerPluginAuto,
Disabled,
}
impl LinkerPluginLto {
pub fn enabled(&self) -> bool {
match *self {
LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true,
LinkerPluginLto::Disabled => false,
}
}
}
#[derive(Default, Clone, PartialEq, Debug)]
pub struct LinkSelfContained {
pub explicitly_set: Option<bool>,
enabled_components: LinkSelfContainedComponents,
disabled_components: LinkSelfContainedComponents,
}
impl LinkSelfContained {
pub(crate) fn set_all_explicitly(&mut self, enabled: bool) {
self.explicitly_set = Some(enabled);
if enabled {
self.enabled_components = LinkSelfContainedComponents::all();
self.disabled_components = LinkSelfContainedComponents::empty();
} else {
self.enabled_components = LinkSelfContainedComponents::empty();
self.disabled_components = LinkSelfContainedComponents::all();
}
}
pub fn on() -> Self {
let mut on = LinkSelfContained::default();
on.set_all_explicitly(true);
on
}
pub fn is_linker_enabled(&self) -> bool {
self.enabled_components.contains(LinkSelfContainedComponents::LINKER)
}
pub fn is_linker_disabled(&self) -> bool {
self.disabled_components.contains(LinkSelfContainedComponents::LINKER)
}
}
#[derive(Default, Copy, Clone, PartialEq, Debug)]
pub struct LinkerFeaturesCli {
pub enabled: LinkerFeatures,
pub disabled: LinkerFeatures,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum IncrementalStateAssertion {
Loaded,
NotLoaded,
}
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
pub struct LocationDetail {
pub file: bool,
pub line: bool,
pub column: bool,
}
impl LocationDetail {
pub(crate) fn all() -> Self {
Self { file: true, line: true, column: true }
}
}
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
pub enum FmtDebug {
Full,
Shallow,
None,
}
impl FmtDebug {
pub(crate) fn all() -> [Symbol; 3] {
[sym::full, sym::none, sym::shallow]
}
}
#[derive(Clone, PartialEq, Hash, Debug, Encodable, Decodable)]
pub enum SwitchWithOptPath {
Enabled(Option<PathBuf>),
Disabled,
}
impl SwitchWithOptPath {
pub fn enabled(&self) -> bool {
match *self {
SwitchWithOptPath::Enabled(_) => true,
SwitchWithOptPath::Disabled => false,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, StableHash)]
#[derive(Encodable, BlobDecodable)]
pub enum SymbolManglingVersion {
Legacy,
V0,
Hashed,
}
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum DebugInfo {
None,
LineDirectivesOnly,
LineTablesOnly,
Limited,
Full,
}
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum DebugInfoCompression {
None,
Zlib,
Zstd,
}
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum MirStripDebugInfo {
None,
LocalsInTinyFunctions,
AllLocals,
}
#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable)]
pub enum SplitDwarfKind {
Single,
Split,
}
impl FromStr for SplitDwarfKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
Ok(match s {
"single" => SplitDwarfKind::Single,
"split" => SplitDwarfKind::Split,
_ => return Err(()),
})
}
}
macro_rules! define_output_types {
(
$(
$(#[doc = $doc:expr])*
$Variant:ident => {
shorthand: $shorthand:expr,
extension: $extension:expr,
description: $description:expr,
default_filename: $default_filename:expr,
is_text: $is_text:expr,
compatible_with_cgus_and_single_output: $compatible:expr
}
),* $(,)?
) => {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, StableHash)]
#[derive(Encodable, Decodable)]
pub enum OutputType {
$(
$(#[doc = $doc])*
$Variant,
)*
}
impl StableOrd for OutputType {
const CAN_USE_UNSTABLE_SORT: bool = true;
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl OutputType {
pub fn iter_all() -> impl Iterator<Item = OutputType> {
static ALL_VARIANTS: &[OutputType] = &[
$(
OutputType::$Variant,
)*
];
ALL_VARIANTS.iter().copied()
}
pub fn shorthand(&self) -> &'static str {
match *self {
$(
OutputType::$Variant => $shorthand,
)*
}
}
pub fn extension(&self) -> &'static str {
match *self {
$(
OutputType::$Variant => $extension,
)*
}
}
pub fn is_text_output(&self) -> bool {
match *self {
$(
OutputType::$Variant => $is_text,
)*
}
}
pub fn description(&self) -> &'static str {
match *self {
$(
OutputType::$Variant => $description,
)*
}
}
pub fn default_filename(&self) -> &'static str {
match *self {
$(
OutputType::$Variant => $default_filename,
)*
}
}
}
}
}
define_output_types! {
Assembly => {
shorthand: "asm",
extension: "s",
description: "Generates a file with the crate's assembly code",
default_filename: "CRATE_NAME.s",
is_text: true,
compatible_with_cgus_and_single_output: false
},
#[doc = "This is the optimized bitcode, which could be either pre-LTO or non-LTO bitcode,"]
#[doc = "depending on the specific request type."]
Bitcode => {
shorthand: "llvm-bc",
extension: "bc",
description: "Generates a binary file containing the LLVM bitcode",
default_filename: "CRATE_NAME.bc",
is_text: false,
compatible_with_cgus_and_single_output: false
},
DepInfo => {
shorthand: "dep-info",
extension: "d",
description: "Generates a file with Makefile syntax that indicates all the source files that were loaded to generate the crate",
default_filename: "CRATE_NAME.d",
is_text: true,
compatible_with_cgus_and_single_output: true
},
Exe => {
shorthand: "link",
extension: "",
description: "Generates the crates specified by --crate-type. This is the default if --emit is not specified",
default_filename: "(platform and crate-type dependent)",
is_text: false,
compatible_with_cgus_and_single_output: true
},
LlvmAssembly => {
shorthand: "llvm-ir",
extension: "ll",
description: "Generates a file containing LLVM IR",
default_filename: "CRATE_NAME.ll",
is_text: true,
compatible_with_cgus_and_single_output: false
},
Metadata => {
shorthand: "metadata",
extension: "rmeta",
description: "Generates a file containing metadata about the crate",
default_filename: "libCRATE_NAME.rmeta",
is_text: false,
compatible_with_cgus_and_single_output: true
},
Mir => {
shorthand: "mir",
extension: "mir",
description: "Generates a file containing rustc's mid-level intermediate representation",
default_filename: "CRATE_NAME.mir",
is_text: true,
compatible_with_cgus_and_single_output: false
},
Object => {
shorthand: "obj",
extension: "o",
description: "Generates a native object file",
default_filename: "CRATE_NAME.o",
is_text: false,
compatible_with_cgus_and_single_output: false
},
#[doc = "This is the summary or index data part of the ThinLTO bitcode."]
ThinLinkBitcode => {
shorthand: "thin-link-bitcode",
extension: "indexing.o",
description: "Generates the ThinLTO summary as bitcode",
default_filename: "CRATE_NAME.indexing.o",
is_text: false,
compatible_with_cgus_and_single_output: false
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorOutputType {
HumanReadable { kind: HumanReadableErrorType, color_config: ColorConfig },
Json {
pretty: bool,
json_rendered: HumanReadableErrorType,
color_config: ColorConfig,
},
}
impl Default for ErrorOutputType {
fn default() -> Self {
ErrorOutputType::HumanReadable {
kind: HumanReadableErrorType { short: false, unicode: false },
color_config: ColorConfig::Auto,
}
}
}
#[derive(Clone, Hash, Debug)]
pub enum ResolveDocLinks {
None,
ExportedMetadata,
Exported,
All,
}
#[derive(Clone, Debug, Hash, StableHash, Encodable, Decodable)]
pub struct OutputTypes(BTreeMap<OutputType, Option<OutFileName>>);
impl OutputTypes {
pub fn new(entries: &[(OutputType, Option<OutFileName>)]) -> OutputTypes {
OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone()))))
}
pub(crate) fn get(&self, key: &OutputType) -> Option<&Option<OutFileName>> {
self.0.get(key)
}
pub fn contains_key(&self, key: &OutputType) -> bool {
self.0.contains_key(key)
}
pub fn contains_explicit_name(&self, key: &OutputType) -> bool {
matches!(self.0.get(key), Some(Some(..)))
}
pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option<OutFileName>> {
self.0.iter()
}
pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<OutFileName>> {
self.0.keys()
}
pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<OutFileName>> {
self.0.values()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn should_codegen(&self) -> bool {
self.0.keys().any(|k| match *k {
OutputType::Bitcode
| OutputType::ThinLinkBitcode
| OutputType::Assembly
| OutputType::LlvmAssembly
| OutputType::Mir
| OutputType::Object
| OutputType::Exe => true,
OutputType::Metadata | OutputType::DepInfo => false,
})
}
pub fn should_link(&self) -> bool {
self.0.keys().any(|k| match *k {
OutputType::Bitcode
| OutputType::ThinLinkBitcode
| OutputType::Assembly
| OutputType::LlvmAssembly
| OutputType::Mir
| OutputType::Metadata
| OutputType::Object
| OutputType::DepInfo => false,
OutputType::Exe => true,
})
}
}
#[derive(Clone)]
pub struct Externs(BTreeMap<String, ExternEntry>);
#[derive(Clone, Debug)]
pub struct ExternEntry {
pub location: ExternLocation,
pub is_private_dep: bool,
pub add_prelude: bool,
pub nounused_dep: bool,
pub force: bool,
}
#[derive(Clone, Debug)]
pub enum ExternLocation {
FoundInLibrarySearchDirectories,
ExactPaths(BTreeSet<CanonicalizedPath>),
}
impl Externs {
pub fn new(data: BTreeMap<String, ExternEntry>) -> Externs {
Externs(data)
}
pub fn get(&self, key: &str) -> Option<&ExternEntry> {
self.0.get(key)
}
pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> {
self.0.iter()
}
}
impl ExternEntry {
pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> {
match &self.location {
ExternLocation::ExactPaths(set) => Some(set.iter()),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct NextSolverConfig {
pub coherence: bool,
pub globally: bool,
}
impl Default for NextSolverConfig {
fn default() -> Self {
if option_env!("CFG_DEFAULT_NEXT_SOLVER_GLOBALLY").is_some() {
Self { coherence: true, globally: true }
} else {
Self { coherence: true, globally: false }
}
}
}
#[derive(Clone)]
pub enum Input {
File(PathBuf),
Str {
name: FileName,
input: String,
},
}
impl Input {
pub fn filestem(&self) -> &str {
if let Input::File(ifile) = self {
if let Some(name) = ifile.file_stem().and_then(Path::to_str) {
return name;
}
}
"rust_out"
}
pub fn file_name(&self, session: &Session) -> FileName {
match *self {
Input::File(ref ifile) => FileName::Real(
session
.psess
.source_map()
.path_mapping()
.to_real_filename(session.psess.source_map().working_dir(), ifile.as_path()),
),
Input::Str { ref name, .. } => name.clone(),
}
}
pub fn opt_path(&self) -> Option<&Path> {
match self {
Input::File(file) => Some(file),
Input::Str { name, .. } => match name {
FileName::Real(real) => real.local_path(),
FileName::CfgSpec(_) => None,
FileName::Anon(_) => None,
FileName::MacroExpansion(_) => None,
FileName::ProcMacroSourceCode(_) => None,
FileName::CliCrateAttr(_) => None,
FileName::Custom(_) => None,
FileName::DocTest(path, _) => Some(path),
FileName::InlineAsm(_) => None,
},
}
}
}
#[derive(Clone, Hash, Debug, StableHash, PartialEq, Eq, Encodable, Decodable)]
pub enum OutFileName {
Real(PathBuf),
Stdout,
}
impl OutFileName {
pub fn parent(&self) -> Option<&Path> {
match *self {
OutFileName::Real(ref path) => path.parent(),
OutFileName::Stdout => None,
}
}
pub fn filestem(&self) -> Option<&Path> {
match *self {
OutFileName::Real(ref path) => path.file_stem(),
OutFileName::Stdout => Some(Path::new("stdout")),
}
}
pub fn is_stdout(&self) -> bool {
match *self {
OutFileName::Real(_) => false,
OutFileName::Stdout => true,
}
}
pub fn is_tty(&self) -> bool {
match *self {
OutFileName::Real(_) => false,
OutFileName::Stdout => eko::file::stdout().is_terminal(),
}
}
pub fn as_path(&self) -> &Path {
match *self {
OutFileName::Real(ref path) => path.as_ref(),
OutFileName::Stdout => Path::new("stdout"),
}
}
pub fn file_for_writing(
&self,
outputs: &OutputFilenames,
flavor: OutputType,
codegen_unit_name: &str,
) -> PathBuf {
match *self {
OutFileName::Real(ref path) => path.clone(),
OutFileName::Stdout => outputs.temp_path_for_cgu(flavor, codegen_unit_name),
}
}
pub fn overwrite(&self, content: &str, sess: &Session) {
match self {
OutFileName::Stdout => eko::print!("{content}"),
OutFileName::Real(path) => {
if let Err(e) = eko::file::write(path, content.as_bytes()) {
sess.dcx().emit_fatal(FileWriteFail { path, err: e.to_string() });
}
}
}
}
}
#[derive(Clone, Hash, Debug, StableHash, Encodable, Decodable)]
pub struct OutputFilenames {
pub(crate) out_directory: PathBuf,
crate_stem: String,
filestem: String,
pub single_output_file: Option<OutFileName>,
temps_directory: Option<PathBuf>,
#[stable_hash(ignore)]
invocation_temp: Option<String>,
explicit_dwo_out_directory: Option<PathBuf>,
pub outputs: OutputTypes,
}
pub const RLINK_EXT: &str = "rlink";
pub const RUST_CGU_EXT: &str = "rcgu";
pub const DWARF_OBJECT_EXT: &str = "dwo";
pub const MAX_FILENAME_LENGTH: usize = 143;
fn maybe_strip_file_name(mut path: PathBuf) -> PathBuf {
if path.file_name().map_or(0, |name| name.len()) > MAX_FILENAME_LENGTH {
let filename = path.file_name().unwrap().to_string_lossy();
let hash_len = 64 / 4; let hyphen_len = 1;
let allowed_suffix = MAX_FILENAME_LENGTH.saturating_sub(hash_len + hyphen_len);
let stripped_bytes = filename.len().saturating_sub(allowed_suffix);
let split_at = filename.ceil_char_boundary(stripped_bytes);
let mut hasher = StableHasher::new();
filename[..split_at].hash(&mut hasher);
let hash = hasher.finish::<Hash64>();
path.set_file_name(format!("{:x}-{}", hash, &filename[split_at..]));
}
path
}
impl OutputFilenames {
pub fn new(
out_directory: PathBuf,
out_crate_name: String,
out_filestem: String,
single_output_file: Option<OutFileName>,
temps_directory: Option<PathBuf>,
invocation_temp: Option<String>,
explicit_dwo_out_directory: Option<PathBuf>,
extra: String,
outputs: OutputTypes,
) -> Self {
OutputFilenames {
out_directory,
single_output_file,
temps_directory,
invocation_temp,
explicit_dwo_out_directory,
outputs,
crate_stem: format!("{out_crate_name}{extra}"),
filestem: format!("{out_filestem}{extra}"),
}
}
pub fn path(&self, flavor: OutputType) -> OutFileName {
self.outputs
.get(&flavor)
.and_then(|p| p.to_owned())
.or_else(|| self.single_output_file.clone())
.unwrap_or_else(|| OutFileName::Real(self.output_path(flavor)))
}
pub fn interface_path(&self) -> PathBuf {
debug!("using crate_name={} for interface_path", self.crate_stem);
self.out_directory.join(format!("lib{}.rs", self.crate_stem))
}
fn output_path(&self, flavor: OutputType) -> PathBuf {
let extension = flavor.extension();
match flavor {
OutputType::Metadata => {
debug!("using crate_name={} for {extension}", self.crate_stem);
self.out_directory.join(format!("lib{}.{}", self.crate_stem, extension))
}
_ => self.with_directory_and_extension(&self.out_directory, extension),
}
}
pub fn temp_path_for_cgu(&self, flavor: OutputType, codegen_unit_name: &str) -> PathBuf {
let extension = flavor.extension();
self.temp_path_ext_for_cgu(extension, codegen_unit_name)
}
pub fn temp_path_dwo_for_cgu(&self, codegen_unit_name: &str) -> PathBuf {
let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name);
if let Some(dwo_out) = &self.explicit_dwo_out_directory {
let mut o = dwo_out.clone();
o.push(p.file_name().unwrap());
o
} else {
p
}
}
pub fn temp_path_ext_for_cgu(&self, ext: &str, codegen_unit_name: &str) -> PathBuf {
let mut extension = codegen_unit_name.to_string();
if let Some(rng) = &self.invocation_temp {
extension.push('.');
extension.push_str(rng);
}
if !ext.is_empty() {
extension.push('.');
extension.push_str(RUST_CGU_EXT);
extension.push('.');
extension.push_str(ext);
}
let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
maybe_strip_file_name(self.with_directory_and_extension(temps_directory, &extension))
}
pub fn temp_path_for_diagnostic(&self, ext: &str) -> PathBuf {
let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
self.with_directory_and_extension(temps_directory, &ext)
}
pub fn with_extension(&self, extension: &str) -> PathBuf {
self.with_directory_and_extension(&self.out_directory, extension)
}
pub fn with_directory_and_extension(&self, directory: &Path, extension: &str) -> PathBuf {
debug!("using filestem={} for {extension}", self.filestem);
let mut path = directory.join(&self.filestem);
path.set_extension(extension);
path
}
pub fn split_dwarf_path(
&self,
split_debuginfo_kind: SplitDebuginfo,
split_dwarf_kind: SplitDwarfKind,
cgu_name: &str,
) -> Option<PathBuf> {
let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name);
let dwo_out = self.temp_path_dwo_for_cgu(cgu_name);
match (split_debuginfo_kind, split_dwarf_kind) {
(SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None,
(SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => {
Some(obj_out)
}
(SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => {
Some(dwo_out)
}
}
}
}
#[derive(Clone, Debug)]
pub struct Sysroot {
pub explicit: Option<PathBuf>,
pub default: PathBuf,
}
impl Sysroot {
pub fn new(explicit: Option<PathBuf>) -> Sysroot {
Sysroot { explicit, default: filesearch::default_sysroot() }
}
pub fn path(&self) -> &Path {
self.explicit.as_deref().unwrap_or(&self.default)
}
pub fn all_paths(&self) -> impl Iterator<Item = &Path> {
self.explicit.as_deref().into_iter().chain(iter::once(&*self.default))
}
}
pub fn host_tuple() -> &'static str {
(option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE")
}
fn file_path_mapping(
remap_path_prefix: Vec<(PathBuf, PathBuf)>,
remap_cwd_prefix: Option<&Path>,
remap_path_scope: RemapPathScopeComponents,
) -> FilePathMapping {
let cwd_remap = if let Some(to) = remap_cwd_prefix
&& let Some(cwd) = eko::env::current_dir().map(PathBuf::from_bytes)
{
Some((cwd, to.to_path_buf()))
} else {
None
};
FilePathMapping::new(remap_path_prefix.into_iter().chain(cwd_remap).collect(), remap_path_scope)
}
impl Default for Options {
fn default() -> Options {
let unstable_opts = UnstableOptions::default();
let working_dir = {
let working_dir = PathBuf::from_bytes(
eko::env::current_dir().expect("the process has a working directory"),
);
let file_mapping =
file_path_mapping(Vec::new(), None, RemapPathScopeComponents::empty());
file_mapping.to_real_filename(&RealFileName::empty(), &working_dir)
};
Options {
crate_types: Vec::new(),
optimize: OptLevel::No,
debuginfo: DebugInfo::None,
lint_opts: Vec::new(),
lint_cap: None,
describe_lints: false,
output_types: OutputTypes(BTreeMap::new()),
search_paths: vec![],
sysroot: Sysroot::new(None),
target_triple: TargetTuple::from_tuple(host_tuple()),
test: false,
incremental: None,
unstable_opts,
cg: Default::default(),
error_format: ErrorOutputType::default(),
diagnostic_width: None,
externs: Externs(BTreeMap::new()),
crate_name: None,
libs: Vec::new(),
unstable_features: UnstableFeatures::Disallow,
debug_assertions: true,
actually_rustdoc: false,
resolve_doc_links: ResolveDocLinks::None,
trimmed_def_paths: false,
cli_forced_codegen_units: None,
cli_forced_local_thinlto_off: false,
remap_path_prefix: Vec::new(),
remap_path_scope: RemapPathScopeComponents::all(),
real_rust_source_base_dir: None,
real_rustc_dev_source_base_dir: None,
edition: DEFAULT_EDITION,
json_artifact_notifications: false,
json_timings: false,
json_unused_externs: JsonUnusedExterns::No,
json_future_incompat: false,
pretty: None,
working_dir,
color: ColorConfig::Auto,
logical_env: FxIndexMap::default(),
verbose: false,
target_modifiers: BTreeMap::default(),
mitigation_coverage_map: Default::default(),
jobs: Jobs { frontend: None, backend: None, linker: LinkerJobs::Default },
}
}
}
impl Options {
pub fn build_dep_graph(&self) -> bool {
self.incremental.is_some()
|| self.unstable_opts.dump_dep_graph
|| self.unstable_opts.query_dep_graph
}
pub fn file_path_mapping(&self) -> FilePathMapping {
file_path_mapping(
self.remap_path_prefix.clone(),
self.unstable_opts.remap_cwd_prefix.as_deref(),
self.remap_path_scope,
)
}
pub fn will_create_output_file(&self) -> bool {
!self.unstable_opts.parse_crate_root_only && self.unstable_opts.ls.is_empty() }
#[inline]
pub fn share_generics(&self) -> bool {
match self.unstable_opts.share_generics {
Some(setting) => setting,
None => match self.optimize {
OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true,
OptLevel::More | OptLevel::Aggressive => false,
},
}
}
pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion {
self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::V0)
}
#[inline]
pub fn autodiff_enabled(&self) -> bool {
self.unstable_opts.autodiff.contains(&AutoDiff::Enable)
}
}
impl UnstableOptions {
pub fn dcx_flags(&self, can_emit_warnings: bool) -> DiagCtxtFlags {
DiagCtxtFlags {
can_emit_warnings,
treat_err_as_bug: self.treat_err_as_bug,
eagerly_emit_delayed_bugs: self.eagerly_emit_delayed_bugs,
macro_backtrace: self.macro_backtrace,
deduplicate_diagnostics: self.deduplicate_diagnostics,
track_diagnostics: self.track_diagnostics,
}
}
pub fn src_hash_algorithm(&self, target: &Target) -> SourceFileHashAlgorithm {
self.src_hash_algorithm.unwrap_or_else(|| {
if target.is_like_msvc {
SourceFileHashAlgorithm::Sha256
} else {
SourceFileHashAlgorithm::Md5
}
})
}
pub fn checksum_hash_algorithm(&self) -> Option<SourceFileHashAlgorithm> {
self.checksum_hash_algorithm
}
}
#[derive(Copy, Clone, PartialEq, Hash, Debug, StableHash)]
pub enum EntryFnType {
Main {
sigpipe: u8,
},
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, Encodable, Decodable)]
pub enum Passes {
Some(Vec<String>),
All,
}
#[derive(Clone, Copy, Hash, Debug, PartialEq)]
pub enum PAuthKey {
A,
B,
}
#[derive(Clone, Copy, Hash, Debug, PartialEq)]
pub struct PacRet {
pub leaf: bool,
pub pc: bool,
pub key: PAuthKey,
}
#[derive(Clone, Copy, Hash, Debug, PartialEq, Default)]
pub struct BranchProtection {
pub bti: bool,
pub pac_ret: Option<PacRet>,
pub gcs: bool,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum PointerAuthOption {
Aarch64JumpTableHardening,
AuthTraps,
Calls,
ElfGot,
FunctionPointerTypeDiscrimination,
IndirectGotos,
InitFini,
InitFiniAddressDiscrimination,
Intrinsics,
ReturnAddresses,
TypeInfoVTPtrDisc,
VTPtrAddrDisc,
VTPtrTypeDisc,
}
impl PointerAuthOption {
pub fn parse(s: &str) -> Option<Self> {
match s {
"aarch64-jump-table-hardening" => Some(Self::Aarch64JumpTableHardening),
"auth-traps" => Some(Self::AuthTraps),
"calls" => Some(Self::Calls),
"elf-got" => Some(Self::ElfGot),
"function-pointer-type-discrimination" => Some(Self::FunctionPointerTypeDiscrimination),
"indirect-gotos" => Some(Self::IndirectGotos),
"init-fini" => Some(Self::InitFini),
"init-fini-address-discrimination" => Some(Self::InitFiniAddressDiscrimination),
"intrinsics" => Some(Self::Intrinsics),
"return-addresses" => Some(Self::ReturnAddresses),
"typeinfo-vt-ptr-discrimination" => Some(Self::TypeInfoVTPtrDisc),
"vt-ptr-addr-discrimination" => Some(Self::VTPtrAddrDisc),
"vt-ptr-type-discrimination" => Some(Self::VTPtrTypeDisc),
_ => None,
}
}
}
#[derive(Clone, Copy)]
pub enum LinkerJobs {
Default,
Explicit(NonZero<usize>),
}
impl LinkerJobs {
pub fn limit(self) -> Option<NonZero<usize>> {
match self {
LinkerJobs::Default => None,
LinkerJobs::Explicit(n) => Some(n),
}
}
}
#[derive(Clone, Copy)]
pub struct Jobs {
pub frontend: Option<NonZero<usize>>,
pub backend: Option<NonZero<usize>>,
pub linker: LinkerJobs,
}
pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg {
cfg::disallow_cfgs(sess, &user_cfg);
user_cfg.extend(cfg::default_configuration(sess));
user_cfg
}
pub fn build_target_config(
early_dcx: &EarlyDiagCtxt,
target: &TargetTuple,
sysroot: &Path,
unstable_options: bool,
) -> Target {
match Target::search(target, sysroot, unstable_options) {
Ok((target, warnings)) => {
for warning in warnings.warning_messages() {
early_dcx.early_warn(warning)
}
if !matches!(target.pointer_width, 16 | 32 | 64) {
early_dcx.early_fatal(format!(
"target specification was invalid: unrecognized target-pointer-width {}",
target.pointer_width
))
}
target
}
Err(e) => {
let mut err =
early_dcx.early_struct_fatal(format!("error loading target specification: {e}"));
err.help("run `rustc --print target-list` for a list of built-in targets");
let typed = target.tuple();
let limit = typed.len() / 3 + 1;
if let Some(suggestion) = crate::rustc_target::spec::TARGETS
.iter()
.filter_map(|&t| {
crate::rustc_span::edit_distance::edit_distance_with_substrings(typed, t, limit)
.map(|d| (d, t))
})
.min_by_key(|(d, _)| *d)
.map(|(_, t)| t)
{
err.help(format!("did you mean `{suggestion}`?"));
}
err.emit()
}
}
}
#[derive(Copy, Clone)]
pub enum JsonUnusedExterns {
No,
Silent,
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,
}
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum PpSourceMode {
Normal,
Expanded,
ExpandedIdentified,
ExpandedHygiene,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum PpHirMode {
Normal,
Identified,
Typed,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum PpMode {
Source(PpSourceMode),
AstTree,
AstTreeExpanded,
Hir(PpHirMode),
HirTree,
ThirTree,
ThirFlat,
Mir,
MirCFG,
StableMir,
}
impl PpMode {
pub fn needs_ast_map(&self) -> bool {
use PpMode::*;
use PpSourceMode::*;
match *self {
Source(Normal) | AstTree => false,
Source(Expanded | ExpandedIdentified | ExpandedHygiene)
| AstTreeExpanded
| Hir(_)
| HirTree
| ThirTree
| ThirFlat
| Mir
| MirCFG
| StableMir => true,
}
}
pub fn needs_analysis(&self) -> bool {
use PpMode::*;
matches!(*self, Hir(PpHirMode::Typed) | Mir | StableMir | MirCFG | ThirTree | ThirFlat)
}
}
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub enum WasiExecModel {
Command,
Reactor,
}
pub(crate) mod dep_tracking {
use alloc::string::String;
use alloc::vec::Vec;
use alloc::collections::BTreeMap;
use core::hash::Hash;
use core::num::NonZero;
use eko::path::PathBuf;
use crate::rustc_abi::Align;
use crate::rustc_ast::attr::version::RustcVersion;
use crate::rustc_data_structures::fx::FxIndexMap;
use crate::rustc_data_structures::stable_hash::StableHasher;
use crate::rustc_feature::UnstableFeatures;
use crate::rustc_hashes::Hash64;
use crate::rustc_span::edition::Edition;
use crate::rustc_span::{RealFileName, RemapPathScopeComponents};
use crate::rustc_structures::CollapseMacroDebuginfo;
use crate::rustc_target::spec::{
CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel,
RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple,
TlsModel,
};
use super::{
AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CodegenRetagOptions,
CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug,
FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentMcount,
InstrumentMcountOpts, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli,
MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, OutFileName, OutputType,
OutputTypes, PatchableFunctionEntry, PointerAuthOption, Polonius, ResolveDocLinks,
SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion,
WasiExecModel,
};
use crate::rustc_session::lint;
use crate::rustc_session::utils::NativeLib;
pub(crate) trait DepTrackingHash {
fn hash(
&self,
hasher: &mut StableHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
);
}
macro_rules! impl_dep_tracking_hash_via_hash {
($($t:ty),+ $(,)?) => {$(
impl DepTrackingHash for $t {
fn hash(&self, hasher: &mut StableHasher, _: ErrorOutputType, _for_crate_hash: bool) {
Hash::hash(self, hasher);
}
}
)+};
}
impl<T: DepTrackingHash> DepTrackingHash for Option<T> {
fn hash(
&self,
hasher: &mut StableHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
match self {
Some(x) => {
Hash::hash(&1, hasher);
DepTrackingHash::hash(x, hasher, error_format, for_crate_hash);
}
None => Hash::hash(&0, hasher),
}
}
}
impl_dep_tracking_hash_via_hash!(
(),
AnnotateMoves,
AutoDiff,
Offload,
bool,
usize,
NonZero<usize>,
u64,
Hash64,
String,
PathBuf,
lint::Level,
WasiExecModel,
u32,
FramePointer,
RelocModel,
CodeModel,
TlsModel,
InstrumentCoverage,
CoverageOptions,
InstrumentMcount,
InstrumentMcountOpts,
InstrumentXRay,
CrateType,
MergeFunctions,
OnBrokenPipe,
PanicStrategy,
RelroLevel,
OptLevel,
LtoCli,
DebugInfo,
DebugInfoCompression,
MirStripDebugInfo,
CollapseMacroDebuginfo,
UnstableFeatures,
NativeLib,
SanitizerSet,
CFGuard,
CFProtection,
TargetTuple,
Edition,
LinkerPluginLto,
ResolveDocLinks,
SplitDebuginfo,
SplitDwarfKind,
StackProtector,
SwitchWithOptPath,
SymbolManglingVersion,
SymbolVisibility,
RemapPathScopeComponents,
SourceFileHashAlgorithm,
OutFileName,
OutputType,
RealFileName,
LocationDetail,
FmtDebug,
BranchProtection,
NextSolverConfig,
PatchableFunctionEntry,
Polonius,
InliningThreshold,
FunctionReturn,
Align,
CodegenRetagOptions,
RustcVersion,
PointerAuthOption,
);
impl<T1, T2> DepTrackingHash for (T1, T2)
where
T1: DepTrackingHash,
T2: DepTrackingHash,
{
fn hash(
&self,
hasher: &mut StableHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
Hash::hash(&0, hasher);
DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
Hash::hash(&1, hasher);
DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
}
}
impl<T1, T2, T3> DepTrackingHash for (T1, T2, T3)
where
T1: DepTrackingHash,
T2: DepTrackingHash,
T3: DepTrackingHash,
{
fn hash(
&self,
hasher: &mut StableHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
Hash::hash(&0, hasher);
DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
Hash::hash(&1, hasher);
DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
Hash::hash(&2, hasher);
DepTrackingHash::hash(&self.2, hasher, error_format, for_crate_hash);
}
}
impl<T: DepTrackingHash> DepTrackingHash for Vec<T> {
fn hash(
&self,
hasher: &mut StableHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
Hash::hash(&self.len(), hasher);
for (index, elem) in self.iter().enumerate() {
Hash::hash(&index, hasher);
DepTrackingHash::hash(elem, hasher, error_format, for_crate_hash);
}
}
}
impl<T: DepTrackingHash, V: DepTrackingHash> DepTrackingHash for FxIndexMap<T, V> {
fn hash(
&self,
hasher: &mut StableHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
Hash::hash(&self.len(), hasher);
for (key, value) in self.iter() {
DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
DepTrackingHash::hash(value, hasher, error_format, for_crate_hash);
}
}
}
impl DepTrackingHash for OutputTypes {
fn hash(
&self,
hasher: &mut StableHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
Hash::hash(&self.0.len(), hasher);
for (key, val) in &self.0 {
DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
if !for_crate_hash {
DepTrackingHash::hash(val, hasher, error_format, for_crate_hash);
}
}
}
}
pub(crate) fn stable_hash(
sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>,
hasher: &mut StableHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
for (key, sub_hash) in sub_hashes {
Hash::hash(&key.len(), hasher);
Hash::hash(key, hasher);
sub_hash.hash(hasher, error_format, for_crate_hash);
}
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum ProcMacroExecutionStrategy {
SameThread,
CrossThread,
}
#[derive(Clone, PartialEq, Hash, Debug, Default)]
pub struct PatchableFunctionEntry {
prefix: u8,
entry: u8,
section: Option<String>,
}
impl PatchableFunctionEntry {
pub fn from_parts(
total_nops: u8,
prefix_nops: u8,
section: Option<String>,
) -> Option<PatchableFunctionEntry> {
if total_nops < prefix_nops {
None
} else if section.as_ref().map(|x| x.contains('\0') || x.is_empty()).unwrap_or(false) {
None
} else {
Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops, section })
}
}
pub fn prefix(&self) -> u8 {
self.prefix
}
pub fn entry(&self) -> u8 {
self.entry
}
pub fn section(&self) -> Option<&str> {
self.section.as_ref().map(|x| x.as_str())
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum Polonius {
Off,
Legacy,
Next,
}
impl Default for Polonius {
fn default() -> Self {
Self::DEFAULT
}
}
impl Polonius {
pub(crate) const DEFAULT: Self =
if option_env!("CFG_DEFAULT_POLONIUS_NEXT").is_some() { Self::Next } else { Self::Off };
pub fn is_legacy_enabled(&self) -> bool {
matches!(self, Polonius::Legacy)
}
pub fn is_next_enabled(&self) -> bool {
matches!(self, Polonius::Next)
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum InliningThreshold {
Always,
Sometimes(usize),
Never,
}
impl Default for InliningThreshold {
fn default() -> Self {
Self::Sometimes(100)
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
pub enum FunctionReturn {
#[default]
Keep,
ThunkExtern,
}
#[derive(Clone, Copy, Default, PartialEq, Debug)]
pub enum MirIncludeSpans {
Off,
On,
#[default]
Nll,
}
impl MirIncludeSpans {
pub fn is_enabled(self) -> bool {
self == MirIncludeSpans::On
}
}