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, OutputType, PrintRequest, SanitizerSet, SwitchWithOptPath};
use crate::filesearch;
use crate::lint;
use crate::parse::ParseSess;
use crate::search_paths::{PathKind, SearchPath};
pub use rustc_ast::attr::MarkedAttrs;
pub use rustc_ast::crate_disambiguator::CrateDisambiguator;
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::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported};
use rustc_span::edition::Edition;
use rustc_span::source_map::{FileLoader, MultiSpan, 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::{Target, TargetTriple, TlsModel};
use std::cell::{self, RefCell};
use std::env;
use std::fmt;
use std::io::Write;
use std::num::NonZeroU32;
use std::ops::{Div, Mul};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
pub struct OptimizationFuel {
remaining: u64,
out_of_fuel: bool,
}
#[derive(Clone, Copy)]
pub enum CtfeBacktrace {
Disabled,
Capture,
Immediate,
}
#[derive(Clone, Copy, Debug)]
pub struct Limit(pub usize);
impl Limit {
pub fn new(value: usize) -> Self {
Limit(value)
}
pub fn value_within_limit(&self, value: usize) -> bool {
value <= self.0
}
}
impl fmt::Display for Limit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
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)
}
}
pub struct Session {
pub target: config::Config,
pub host: Target,
pub opts: config::Options,
pub host_tlib_path: SearchPath,
pub target_tlib_path: Option<SearchPath>,
pub parse_sess: ParseSess,
pub sysroot: PathBuf,
pub local_crate_source_file: Option<PathBuf>,
pub working_dir: (PathBuf, bool),
pub one_time_diagnostics: Lock<FxHashSet<(DiagnosticMessageId, Option<Span>, String)>>,
crate_types: OnceCell<Vec<CrateType>>,
pub crate_disambiguator: OnceCell<CrateDisambiguator>,
features: OnceCell<rustc_feature::Features>,
pub recursion_limit: OnceCell<Limit>,
pub type_length_limit: OnceCell<Limit>,
pub const_eval_limit: OnceCell<Limit>,
incr_comp_session: OneThread<RefCell<IncrCompSession>>,
pub cgu_reuse_tracker: CguReuseTracker,
pub prof: SelfProfilerRef,
pub perf_stats: PerfStats,
pub code_stats: CodeStats,
optimization_fuel_crate: Option<String>,
optimization_fuel: Lock<OptimizationFuel>,
pub print_fuel_crate: Option<String>,
pub print_fuel: AtomicU64,
pub jobserver: Client,
pub driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
pub trait_methods_not_found: Lock<FxHashSet<Span>>,
pub confused_type_with_std_module: Lock<FxHashMap<Span, Span>>,
pub system_library_path: OneThread<RefCell<Option<Option<PathBuf>>>>,
pub ctfe_backtrace: Lock<CtfeBacktrace>,
miri_unleashed_features: Lock<Vec<(Span, Option<Symbol>)>>,
pub real_rust_source_base_dir: Option<PathBuf>,
pub asm_arch: Option<InlineAsmArch>,
pub target_features: FxHashSet<Symbol>,
known_attrs: Lock<MarkedAttrs>,
used_attrs: Lock<MarkedAttrs>,
pub if_let_suggestions: Lock<FxHashSet<Span>>,
}
pub struct PerfStats {
pub symbol_hash_time: Lock<Duration>,
pub queries_canonicalized: AtomicUsize,
pub normalize_generic_arg_after_erasing_regions: AtomicUsize,
pub normalize_projection_ty: AtomicUsize,
}
enum DiagnosticBuilderMethod {
Note,
SpanNote,
SpanSuggestion(String),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DiagnosticMessageId {
ErrorId(u16),
LintId(lint::LintId),
StabilityId(Option<NonZeroU32>),
}
impl From<&'static lint::Lint> for DiagnosticMessageId {
fn from(lint: &'static lint::Lint) -> Self {
DiagnosticMessageId::LintId(lint::LintId::of(lint))
}
}
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;
let mut diag = self.struct_warn("skipping const checks");
for &(span, feature_gate) in unleashed_features.iter() {
if let Some(feature_gate) = feature_gate {
diag.span_help(span, &format!("skipping check for `{}` feature", feature_gate));
must_err = true;
} else {
diag.span_help(span, "skipping check that does not even have a feature gate");
}
}
diag.emit();
if must_err && !self.has_errors() {
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",
);
}
}
}
pub fn finish_diagnostics(&self, registry: &Registry) {
self.check_miri_unleashed_features();
self.diagnostic().print_error_count(registry);
}
pub fn local_crate_disambiguator(&self) -> CrateDisambiguator {
self.crate_disambiguator.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")
}
pub fn recursion_limit(&self) -> Limit {
self.recursion_limit.get().copied().unwrap()
}
pub fn type_length_limit(&self) -> Limit {
self.type_length_limit.get().copied().unwrap()
}
pub fn const_eval_limit(&self) -> Limit {
self.const_eval_limit.get().copied().unwrap()
}
pub fn struct_span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_span_warn(sp, msg)
}
pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
code: DiagnosticId,
) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_span_warn_with_code(sp, msg, code)
}
pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_warn(msg)
}
pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_span_err(sp, msg)
}
pub fn struct_span_err_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
code: DiagnosticId,
) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_span_err_with_code(sp, msg, code)
}
pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_err(msg)
}
pub fn struct_err_with_code(&self, msg: &str, code: DiagnosticId) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_err_with_code(msg, code)
}
pub fn struct_span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_span_fatal(sp, msg)
}
pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
code: DiagnosticId,
) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_span_fatal_with_code(sp, msg, code)
}
pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_fatal(msg)
}
pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.diagnostic().span_fatal(sp, msg).raise()
}
pub fn span_fatal_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
code: DiagnosticId,
) -> ! {
self.diagnostic().span_fatal_with_code(sp, msg, code).raise()
}
pub fn fatal(&self, msg: &str) -> ! {
self.diagnostic().fatal(msg).raise()
}
pub fn span_err_or_warn<S: Into<MultiSpan>>(&self, is_warning: bool, sp: S, msg: &str) {
if is_warning {
self.span_warn(sp, msg);
} else {
self.span_err(sp, msg);
}
}
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.diagnostic().span_err(sp, msg)
}
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
self.diagnostic().span_err_with_code(sp, &msg, code)
}
pub fn err(&self, msg: &str) {
self.diagnostic().err(msg)
}
pub fn err_count(&self) -> usize {
self.diagnostic().err_count()
}
pub fn has_errors(&self) -> bool {
self.diagnostic().has_errors()
}
pub fn has_errors_or_delayed_span_bugs(&self) -> bool {
self.diagnostic().has_errors_or_delayed_span_bugs()
}
pub fn abort_if_errors(&self) {
self.diagnostic().abort_if_errors();
}
pub fn compile_status(&self) -> Result<(), ErrorReported> {
if self.has_errors() {
self.diagnostic().emit_stashed_diagnostics();
Err(ErrorReported)
} else {
Ok(())
}
}
pub fn track_errors<F, T>(&self, f: F) -> Result<T, ErrorReported>
where
F: FnOnce() -> T,
{
let old_count = self.err_count();
let result = f();
let errors = self.err_count() - old_count;
if errors == 0 { Ok(result) } else { Err(ErrorReported) }
}
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.diagnostic().span_warn(sp, msg)
}
pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
self.diagnostic().span_warn_with_code(sp, msg, code)
}
pub fn warn(&self, msg: &str) {
self.diagnostic().warn(msg)
}
pub fn opt_span_warn<S: Into<MultiSpan>>(&self, opt_sp: Option<S>, msg: &str) {
match opt_sp {
Some(sp) => self.span_warn(sp, msg),
None => self.warn(msg),
}
}
#[track_caller]
pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.diagnostic().delay_span_bug(sp, msg)
}
pub fn note_without_error(&self, msg: &str) {
self.diagnostic().note_without_error(msg)
}
pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.diagnostic().span_note_without_error(sp, msg)
}
pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_> {
self.diagnostic().struct_note_without_error(msg)
}
pub fn diagnostic(&self) -> &rustc_errors::Handler {
&self.parse_sess.span_diagnostic
}
fn diag_once<'a, 'b>(
&'a self,
diag_builder: &'b mut DiagnosticBuilder<'a>,
method: DiagnosticBuilderMethod,
msg_id: DiagnosticMessageId,
message: &str,
span_maybe: Option<Span>,
) {
let id_span_message = (msg_id, span_maybe, message.to_owned());
let fresh = self.one_time_diagnostics.borrow_mut().insert(id_span_message);
if fresh {
match method {
DiagnosticBuilderMethod::Note => {
diag_builder.note(message);
}
DiagnosticBuilderMethod::SpanNote => {
let span = span_maybe.expect("`span_note` needs a span");
diag_builder.span_note(span, message);
}
DiagnosticBuilderMethod::SpanSuggestion(suggestion) => {
let span = span_maybe.expect("`span_suggestion_*` needs a span");
diag_builder.span_suggestion(
span,
message,
suggestion,
Applicability::Unspecified,
);
}
}
}
}
pub fn diag_span_note_once<'a, 'b>(
&'a self,
diag_builder: &'b mut DiagnosticBuilder<'a>,
msg_id: DiagnosticMessageId,
span: Span,
message: &str,
) {
self.diag_once(
diag_builder,
DiagnosticBuilderMethod::SpanNote,
msg_id,
message,
Some(span),
);
}
pub fn diag_note_once<'a, 'b>(
&'a self,
diag_builder: &'b mut DiagnosticBuilder<'a>,
msg_id: DiagnosticMessageId,
message: &str,
) {
self.diag_once(diag_builder, DiagnosticBuilderMethod::Note, msg_id, message, None);
}
pub fn diag_span_suggestion_once<'a, 'b>(
&'a self,
diag_builder: &'b mut DiagnosticBuilder<'a>,
msg_id: DiagnosticMessageId,
span: Span,
message: &str,
suggestion: String,
) {
self.diag_once(
diag_builder,
DiagnosticBuilderMethod::SpanSuggestion(suggestion),
msg_id,
message,
Some(span),
);
}
#[inline]
pub fn source_map(&self) -> &SourceMap {
self.parse_sess.source_map()
}
pub fn verbose(&self) -> bool {
self.opts.debugging_opts.verbose
}
pub fn time_passes(&self) -> bool {
self.opts.debugging_opts.time_passes || self.opts.debugging_opts.time
}
pub fn instrument_mcount(&self) -> bool {
self.opts.debugging_opts.instrument_mcount
}
pub fn time_llvm_passes(&self) -> bool {
self.opts.debugging_opts.time_llvm_passes
}
pub fn meta_stats(&self) -> bool {
self.opts.debugging_opts.meta_stats
}
pub fn asm_comments(&self) -> bool {
self.opts.debugging_opts.asm_comments
}
pub fn verify_llvm_ir(&self) -> bool {
self.opts.debugging_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some()
}
pub fn borrowck_stats(&self) -> bool {
self.opts.debugging_opts.borrowck_stats
}
pub fn print_llvm_passes(&self) -> bool {
self.opts.debugging_opts.print_llvm_passes
}
pub fn binary_dep_depinfo(&self) -> bool {
self.opts.debugging_opts.binary_dep_depinfo
}
#[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 lto(&self) -> config::Lto {
if self.target.target.options.requires_lto {
return config::Lto::Fat;
}
match self.opts.cg.lto {
config::LtoCli::Unspecified => {
}
config::LtoCli::No => {
return config::Lto::No;
}
config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => {
return config::Lto::Fat;
}
config::LtoCli::Thin => {
return if self.opts.cli_forced_thinlto_off {
config::Lto::Fat
} else {
config::Lto::Thin
};
}
}
if self.opts.cli_forced_thinlto_off {
return config::Lto::No;
}
if let Some(enabled) = self.opts.debugging_opts.thinlto {
if enabled {
return config::Lto::ThinLocal;
} else {
return config::Lto::No;
}
}
if self.codegen_units() == 1 {
return config::Lto::No;
}
match self.opts.optimize {
config::OptLevel::No => config::Lto::No,
_ => config::Lto::ThinLocal,
}
}
pub fn panic_strategy(&self) -> PanicStrategy {
self.opts.cg.panic.unwrap_or(self.target.target.options.panic_strategy)
}
pub fn fewer_names(&self) -> bool {
let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly)
|| self.opts.output_types.contains_key(&OutputType::Bitcode)
|| self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY);
self.opts.debugging_opts.fewer_names || !more_names
}
pub fn unstable_options(&self) -> bool {
self.opts.debugging_opts.unstable_options
}
pub fn overflow_checks(&self) -> bool {
self.opts
.cg
.overflow_checks
.or(self.opts.debugging_opts.force_overflow_checks)
.unwrap_or(self.opts.debug_assertions)
}
pub fn crt_static(&self, crate_type: Option<CrateType>) -> bool {
if !self.target.target.options.crt_static_respected {
return self.target.target.options.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");
if found_positive || found_negative {
found_positive
} else if crate_type == Some(CrateType::ProcMacro)
|| crate_type == None && self.opts.crate_types.contains(&CrateType::ProcMacro)
{
false
} else {
self.target.target.options.crt_static_default
}
}
pub fn relocation_model(&self) -> RelocModel {
self.opts.cg.relocation_model.unwrap_or(self.target.target.options.relocation_model)
}
pub fn code_model(&self) -> Option<CodeModel> {
self.opts.cg.code_model.or(self.target.target.options.code_model)
}
pub fn tls_model(&self) -> TlsModel {
self.opts.debugging_opts.tls_model.unwrap_or(self.target.target.options.tls_model)
}
pub fn must_not_eliminate_frame_pointers(&self) -> bool {
if self.instrument_mcount() {
true
} else if let Some(x) = self.opts.cg.force_frame_pointers {
x
} else {
!self.target.target.options.eliminate_frame_pointer
}
}
pub fn must_emit_unwind_tables(&self) -> bool {
if self.panic_strategy() == PanicStrategy::Unwind {
true
} else if self.target.target.options.requires_uwtable {
true
} else {
self.opts.cg.force_unwind_tables.unwrap_or(false)
}
}
pub fn generate_plugin_registrar_symbol(&self, disambiguator: CrateDisambiguator) -> String {
format!("__rustc_plugin_registrar_{}__", disambiguator.to_fingerprint().to_hex())
}
pub fn generate_proc_macro_decls_symbol(&self, disambiguator: CrateDisambiguator) -> String {
format!("__rustc_proc_macro_decls_{}__", disambiguator.to_fingerprint().to_hex())
}
pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> {
filesearch::FileSearch::new(
&self.sysroot,
self.opts.target_triple.triple(),
&self.opts.search_paths,
self.target_tlib_path.as_ref().unwrap_or(&self.host_tlib_path),
kind,
)
}
pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> {
filesearch::FileSearch::new(
&self.sysroot,
config::host_triple(),
&self.opts.search_paths,
&self.host_tlib_path,
kind,
)
}
pub fn set_incr_session_load_dep_graph(&self, load: bool) {
let mut incr_comp_session = self.incr_comp_session.borrow_mut();
if let IncrCompSession::Active { ref mut load_dep_graph, .. } = *incr_comp_session {
*load_dep_graph = load;
}
}
pub fn incr_session_load_dep_graph(&self) -> bool {
let incr_comp_session = self.incr_comp_session.borrow();
match *incr_comp_session {
IncrCompSession::Active { load_dep_graph, .. } => load_dep_graph,
_ => false,
}
}
pub fn init_incr_comp_session(
&self,
session_dir: PathBuf,
lock_file: flock::Lock,
load_dep_graph: bool,
) {
let mut incr_comp_session = self.incr_comp_session.borrow_mut();
if let IncrCompSession::NotInitialized = *incr_comp_session {
} else {
panic!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session)
}
*incr_comp_session =
IncrCompSession::Active { session_directory: session_dir, lock_file, load_dep_graph };
}
pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) {
let mut incr_comp_session = self.incr_comp_session.borrow_mut();
if let IncrCompSession::Active { .. } = *incr_comp_session {
} else {
panic!("trying to finalize `IncrCompSession` `{:?}`", *incr_comp_session);
}
*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),
};
*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) {
println!(
"Total time spent computing symbol hashes: {}",
duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock())
);
println!(
"Total queries canonicalized: {}",
self.perf_stats.queries_canonicalized.load(Ordering::Relaxed)
);
println!(
"normalize_generic_arg_after_erasing_regions: {}",
self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed)
);
println!(
"normalize_projection_ty: {}",
self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed)
);
}
pub fn consider_optimizing<T: Fn() -> String>(&self, crate_name: &str, msg: T) -> bool {
let mut ret = true;
if let Some(ref c) = self.optimization_fuel_crate {
if c == crate_name {
assert_eq!(self.threads(), 1);
let mut fuel = self.optimization_fuel.lock();
ret = fuel.remaining != 0;
if fuel.remaining == 0 && !fuel.out_of_fuel {
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.print_fuel_crate {
if c == crate_name {
assert_eq!(self.threads(), 1);
self.print_fuel.fetch_add(1, SeqCst);
}
}
ret
}
pub fn threads(&self) -> usize {
self.opts.debugging_opts.threads
}
pub fn codegen_units(&self) -> usize {
if let Some(n) = self.opts.cli_forced_codegen_units {
return n;
}
if let Some(n) = self.target.target.options.default_codegen_units {
return n as usize;
}
if self.opts.incremental.is_some() {
return 256;
}
16
}
pub fn teach(&self, code: &DiagnosticId) -> bool {
self.opts.debugging_opts.teach && self.diagnostic().must_teach(code)
}
pub fn rust_2015(&self) -> bool {
self.opts.edition == Edition::Edition2015
}
pub fn rust_2018(&self) -> bool {
self.opts.edition >= Edition::Edition2018
}
pub fn edition(&self) -> Edition {
self.opts.edition
}
pub fn needs_plt(&self) -> bool {
let needs_plt = self.target.target.options.needs_plt;
let dbg_opts = &self.opts.debugging_opts;
let relro_level = dbg_opts.relro_level.unwrap_or(self.target.target.options.relro_level);
let full_relro = RelroLevel::Full == relro_level;
dbg_opts.plt.unwrap_or(needs_plt || !full_relro)
}
pub fn emit_lifetime_markers(&self) -> bool {
self.opts.optimize != config::OptLevel::No
|| self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY)
}
pub fn link_dead_code(&self) -> bool {
match self.opts.cg.link_dead_code {
Some(explicitly_set) => explicitly_set,
None => {
self.opts.debugging_opts.instrument_coverage
&& !self.target.target.options.is_like_msvc
}
}
}
pub fn mark_attr_known(&self, attr: &Attribute) {
self.known_attrs.lock().mark(attr)
}
pub fn is_attr_known(&self, attr: &Attribute) -> bool {
self.known_attrs.lock().is_marked(attr)
}
pub fn mark_attr_used(&self, attr: &Attribute) {
self.used_attrs.lock().mark(attr)
}
pub fn is_attr_used(&self, attr: &Attribute) -> bool {
self.used_attrs.lock().is_marked(attr)
}
pub fn check_name(&self, attr: &Attribute, name: Symbol) -> bool {
let matches = attr.has_name(name);
if matches {
self.mark_attr_used(attr);
}
matches
}
pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {
[sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
.iter()
.any(|kind| self.check_name(attr, *kind))
}
pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool {
attrs.iter().any(|item| self.check_name(item, name))
}
pub fn find_by_name<'a>(
&'a self,
attrs: &'a [Attribute],
name: Symbol,
) -> Option<&'a Attribute> {
attrs.iter().find(|attr| self.check_name(attr, name))
}
pub fn filter_by_name<'a>(
&'a self,
attrs: &'a [Attribute],
name: Symbol,
) -> impl Iterator<Item = &'a Attribute> {
attrs.iter().filter(move |attr| self.check_name(attr, name))
}
pub fn first_attr_value_str_by_name(
&self,
attrs: &[Attribute],
name: Symbol,
) -> Option<Symbol> {
attrs.iter().find(|at| self.check_name(at, name)).and_then(|at| at.value_str())
}
}
fn default_emitter(
sopts: &config::Options,
registry: rustc_errors::registry::Registry,
source_map: Lrc<SourceMap>,
emitter_dest: Option<Box<dyn Write + Send>>,
) -> Box<dyn Emitter + sync::Send> {
let macro_backtrace = sopts.debugging_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), short, macro_backtrace);
Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing))
} else {
let emitter = match dst {
None => EmitterWriter::stderr(
color_config,
Some(source_map),
short,
sopts.debugging_opts.teach,
sopts.debugging_opts.terminal_width,
macro_backtrace,
),
Some(dst) => EmitterWriter::new(
dst,
Some(source_map),
short,
false,
false,
None,
macro_backtrace,
),
};
Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing))
}
}
(config::ErrorOutputType::Json { pretty, json_rendered }, None) => Box::new(
JsonEmitter::stderr(
Some(registry),
source_map,
pretty,
json_rendered,
sopts.debugging_opts.terminal_width,
macro_backtrace,
)
.ui_testing(sopts.debugging_opts.ui_testing),
),
(config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new(
JsonEmitter::new(
dst,
Some(registry),
source_map,
pretty,
json_rendered,
sopts.debugging_opts.terminal_width,
macro_backtrace,
)
.ui_testing(sopts.debugging_opts.ui_testing),
),
}
}
pub enum DiagnosticOutput {
Default,
Raw(Box<dyn Write + Send>),
}
pub fn build_session(
sopts: config::Options,
local_crate_source_file: Option<PathBuf>,
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>>,
) -> Session {
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 target_cfg = config::build_target_config(&sopts, sopts.error_format);
let host_triple = TargetTriple::from_triple(config::host_triple());
let host = Target::search(&host_triple).unwrap_or_else(|e| {
early_error(sopts.error_format, &format!("Error loading host specification: {}", e))
});
let loader = file_loader.unwrap_or(Box::new(RealFileLoader));
let hash_kind = sopts.debugging_opts.src_hash_algorithm.unwrap_or_else(|| {
if target_cfg.target.options.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 emitter = default_emitter(&sopts, registry, source_map.clone(), write_dest);
let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags(
emitter,
sopts.debugging_opts.diagnostic_handler_flags(can_emit_warnings),
);
let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.debugging_opts.self_profile
{
let directory =
if let Some(ref directory) = d { directory } else { std::path::Path::new(".") };
let profiler = SelfProfiler::new(
directory,
sopts.crate_name.as_ref().map(|s| &s[..]),
&sopts.debugging_opts.self_profile_events,
);
match profiler {
Ok(profiler) => Some(Arc::new(profiler)),
Err(e) => {
early_warn(sopts.error_format, &format!("failed to create profiler: {}", e));
None
}
}
} else {
None
};
let parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map);
let sysroot = match &sopts.maybe_sysroot {
Some(sysroot) => sysroot.clone(),
None => filesearch::get_or_default_sysroot(),
};
let host_triple = config::host_triple();
let target_triple = sopts.target_triple.triple();
let host_tlib_path = SearchPath::from_sysroot_and_triple(&sysroot, host_triple);
let target_tlib_path = if host_triple == target_triple {
None
} else {
Some(SearchPath::from_sysroot_and_triple(&sysroot, target_triple))
};
let file_path_mapping = sopts.file_path_mapping();
let local_crate_source_file =
local_crate_source_file.map(|path| file_path_mapping.map_prefix(path).0);
let optimization_fuel_crate = sopts.debugging_opts.fuel.as_ref().map(|i| i.0.clone());
let optimization_fuel = Lock::new(OptimizationFuel {
remaining: sopts.debugging_opts.fuel.as_ref().map(|i| i.1).unwrap_or(0),
out_of_fuel: false,
});
let print_fuel_crate = sopts.debugging_opts.print_fuel.clone();
let print_fuel = AtomicU64::new(0);
let working_dir = env::current_dir().unwrap_or_else(|e| {
parse_sess.span_diagnostic.fatal(&format!("Current directory is invalid: {}", e)).raise()
});
let working_dir = file_path_mapping.map_prefix(working_dir);
let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph {
CguReuseTracker::new()
} else {
CguReuseTracker::new_disabled()
};
let prof = SelfProfilerRef::new(
self_profiler,
sopts.debugging_opts.time_passes || sopts.debugging_opts.time,
sopts.debugging_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 real_rust_source_base_dir = {
let mut candidate = sysroot.join("lib/rustlib/src/rust");
if let Ok(metadata) = candidate.symlink_metadata() {
if metadata.file_type().is_symlink() {
if let Ok(symlink_dest) = std::fs::read_link(&candidate) {
candidate = symlink_dest;
}
}
}
if candidate.join("library/std/src/lib.rs").is_file() { Some(candidate) } else { None }
};
let asm_arch = if target_cfg.target.options.allow_asm {
InlineAsmArch::from_str(&target_cfg.target.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,
working_dir,
one_time_diagnostics: Default::default(),
crate_types: OnceCell::new(),
crate_disambiguator: OnceCell::new(),
features: OnceCell::new(),
recursion_limit: OnceCell::new(),
type_length_limit: OnceCell::new(),
const_eval_limit: 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_crate,
optimization_fuel,
print_fuel_crate,
print_fuel,
jobserver: jobserver::client(),
driver_lint_caps,
trait_methods_not_found: Lock::new(Default::default()),
confused_type_with_std_module: Lock::new(Default::default()),
system_library_path: OneThread::new(RefCell::new(Default::default())),
ctfe_backtrace,
miri_unleashed_features: Lock::new(Default::default()),
real_rust_source_base_dir,
asm_arch,
target_features: FxHashSet::default(),
known_attrs: Lock::new(MarkedAttrs::new()),
used_attrs: Lock::new(MarkedAttrs::new()),
if_let_suggestions: Default::default(),
};
validate_commandline_args_with_session_available(&sess);
sess
}
fn validate_commandline_args_with_session_available(sess: &Session) {
if sess.opts.cg.linker_plugin_lto.enabled()
&& sess.opts.cg.prefer_dynamic
&& sess.target.target.options.is_like_windows
{
sess.err(
"Linker plugin based LTO is not supported together with \
`-C prefer-dynamic` when targeting Windows-like targets",
);
}
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()
));
}
}
if let Some(include_uwtables) = sess.opts.cg.force_unwind_tables {
if sess.panic_strategy() == PanicStrategy::Unwind && !include_uwtables {
sess.err(
"panic=unwind requires unwind tables, they cannot be disabled \
with `-C force-unwind-tables=no`.",
);
}
if sess.target.target.options.requires_uwtable && !include_uwtables {
sess.err(
"target requires unwind tables, they cannot be disabled with \
`-C force-unwind-tables=no`.",
);
}
}
if sess.opts.cg.profile_generate.enabled()
&& sess.target.target.options.is_like_msvc
&& sess.panic_strategy() == PanicStrategy::Unwind
&& sess.opts.prints.iter().all(|&p| p == PrintRequest::NativeStaticLibs)
{
sess.err(
"Profile-guided optimization does not yet work in conjunction \
with `-Cpanic=unwind` on Windows when targeting MSVC. \
See issue #61002 <https://github.com/rust-lang/rust/issues/61002> \
for more information.",
);
}
const ASAN_SUPPORTED_TARGETS: &[&str] = &[
"aarch64-fuchsia",
"aarch64-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-fuchsia",
"x86_64-unknown-freebsd",
"x86_64-unknown-linux-gnu",
];
const LSAN_SUPPORTED_TARGETS: &[&str] =
&["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"];
const MSAN_SUPPORTED_TARGETS: &[&str] =
&["aarch64-unknown-linux-gnu", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu"];
const TSAN_SUPPORTED_TARGETS: &[&str] = &[
"aarch64-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-unknown-freebsd",
"x86_64-unknown-linux-gnu",
];
for s in sess.opts.debugging_opts.sanitizer {
let supported_targets = match s {
SanitizerSet::ADDRESS => ASAN_SUPPORTED_TARGETS,
SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS,
SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS,
SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS,
_ => panic!("unrecognized sanitizer {}", s),
};
if !supported_targets.contains(&&*sess.opts.target_triple.triple()) {
sess.err(&format!(
"`-Zsanitizer={}` only works with targets: {}",
s,
supported_targets.join(", ")
));
}
let conflicting = sess.opts.debugging_opts.sanitizer - s;
if !conflicting.is_empty() {
sess.err(&format!(
"`-Zsanitizer={}` is incompatible with `-Zsanitizer={}`",
s, conflicting,
));
break;
}
}
}
#[derive(Debug)]
pub enum IncrCompSession {
NotInitialized,
Active { session_directory: PathBuf, lock_file: flock::Lock, load_dep_graph: bool },
Finalized { session_directory: PathBuf },
InvalidBecauseOfErrors { session_directory: PathBuf },
}
pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
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, short, false, None, false))
}
config::ErrorOutputType::Json { pretty, json_rendered } => {
Box::new(JsonEmitter::basic(pretty, json_rendered, None, false))
}
};
let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
handler.struct_fatal(msg).emit();
rustc_errors::FatalError.raise();
}
pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
let emitter: Box<dyn Emitter + sync::Send> = match output {
config::ErrorOutputType::HumanReadable(kind) => {
let (short, color_config) = kind.unzip();
Box::new(EmitterWriter::stderr(color_config, None, short, false, None, false))
}
config::ErrorOutputType::Json { pretty, json_rendered } => {
Box::new(JsonEmitter::basic(pretty, json_rendered, None, false))
}
};
let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
handler.struct_warn(msg).emit();
}
pub type CompileResult = Result<(), ErrorReported>;