X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2Flibrustc%2Fsession%2Fmod.rs;h=13b76b79b3d82a5301a0dc94800b9046985204be;hb=e74abb32707ea6577e99daad63c09c1d5135f7dc;hp=99a58f07ae62bf6e3c9ec5d0c1b84bcca1a8c056;hpb=c1a9b12dc94607f2d0cec7929de9ba4c085f47f1;p=rustc.git diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 99a58f07ae..13b76b79b3 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -1,463 +1,1344 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use lint; -use metadata::cstore::CStore; -use metadata::filesearch; -use session::search_paths::PathKind; -use util::nodemap::NodeMap; - -use syntax::ast::NodeId; -use syntax::codemap::Span; -use syntax::diagnostic::{self, Emitter}; -use syntax::diagnostics; -use syntax::feature_gate; -use syntax::parse; -use syntax::parse::token; -use syntax::parse::ParseSess; -use syntax::{ast, codemap}; -use syntax::feature_gate::AttributeType; - -use rustc_back::target::Target; - -use std::path::{Path, PathBuf}; -use std::cell::{Cell, RefCell}; +pub use self::code_stats::{DataTypeKind, SizeKind, FieldInfo, VariantInfo}; +use self::code_stats::CodeStats; + +use crate::dep_graph::cgu_reuse_tracker::CguReuseTracker; +use crate::hir::def_id::CrateNum; +use rustc_data_structures::fingerprint::Fingerprint; + +use crate::lint; +use crate::session::config::{OutputType, PrintRequest, Sanitizer, SwitchWithOptPath}; +use crate::session::search_paths::{PathKind, SearchPath}; +use crate::util::nodemap::{FxHashMap, FxHashSet}; +use crate::util::common::{duration_to_secs_str, ErrorReported}; + +use rustc_data_structures::base_n; +use rustc_data_structures::sync::{ + self, Lrc, Lock, OneThread, Once, AtomicU64, AtomicUsize, Ordering, + Ordering::SeqCst, +}; + +use errors::{DiagnosticBuilder, DiagnosticId, Applicability}; +use errors::emitter::{Emitter, EmitterWriter}; +use errors::emitter::HumanReadableErrorType; +use errors::annotate_snippet_emitter_writer::{AnnotateSnippetEmitterWriter}; +use syntax::ast::{self, NodeId}; +use syntax::edition::Edition; +use syntax::expand::allocator::AllocatorKind; +use syntax::feature_gate::{self, AttributeType}; +use syntax::json::JsonEmitter; +use syntax::source_map; +use syntax::sess::ParseSess; +use syntax::symbol::Symbol; +use syntax_pos::{MultiSpan, Span}; +use crate::util::profiling::{SelfProfiler, SelfProfilerRef}; + +use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple}; +use rustc_data_structures::flock; +use rustc_data_structures::jobserver; +use ::jobserver::Client; + +use std; +use std::cell::{self, Cell, RefCell}; use std::env; +use std::fmt; +use std::io::Write; +use std::path::PathBuf; +use std::time::Duration; +use std::sync::Arc; +mod code_stats; pub mod config; +pub mod filesearch; pub mod search_paths; -// Represents the data associated with a compilation -// session for a single crate. +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, +} + +/// Represents the data associated with a compilation +/// session for a single crate. pub struct Session { pub target: config::Config, pub host: Target, pub opts: config::Options, - pub cstore: CStore, + pub host_tlib_path: SearchPath, + /// `None` if the host and target are the same. + pub target_tlib_path: Option, pub parse_sess: ParseSess, - // For a library crate, this is always none - pub entry_fn: RefCell>, - pub entry_type: Cell>, - pub plugin_registrar_fn: Cell>, - pub default_sysroot: Option, - // The name of the root source file of the crate, in the local file system. - // The path is always expected to be absolute. `None` means that there is no - // source file. + 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, - pub working_dir: PathBuf, - pub lint_store: RefCell, - pub lints: RefCell>>, - pub plugin_llvm_passes: RefCell>, - pub plugin_attributes: RefCell>, - pub crate_types: RefCell>, - pub crate_metadata: RefCell>, - pub features: RefCell, + /// The directory the compiler has been executed in plus a flag indicating + /// if the value stored here has been affected by path remapping. + pub working_dir: (PathBuf, bool), - pub delayed_span_bug: RefCell>, + /// Set of `(DiagnosticId, Option, message)` tuples tracking + /// (sub)diagnostics that have been set once, but should not be set again, + /// in order to avoid redundantly verbose output (Issue #24690, #44953). + pub one_time_diagnostics: Lock, String)>>, + pub plugin_llvm_passes: OneThread>>, + pub plugin_attributes: Lock>, + pub crate_types: Once>, + /// The `crate_disambiguator` is constructed out of all the `-C metadata` + /// arguments passed to the compiler. Its value together with the crate-name + /// 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 crate_disambiguator: Once, + + features: Once, /// The maximum recursion limit for potentially infinitely recursive /// operations such as auto-dereference and monomorphization. - pub recursion_limit: Cell, + pub recursion_limit: Once, + + /// The maximum length of types during monomorphization. + pub type_length_limit: Once, + + /// The maximum number of stackframes allowed in const eval. + pub const_eval_stack_frame_limit: usize, + + /// The `metadata::creader` module may inject an allocator/`panic_runtime` + /// dependency if it didn't already find one, and this tracks what was + /// injected. + pub allocator_kind: Once>, + pub injected_panic_runtime: Once>, + + /// Map from imported macro spans (which consist of + /// the localized span for the macro body) to the + /// macro name and definition span in the source crate. + pub imported_macro_spans: OneThread>>, + + incr_comp_session: OneThread>, + /// 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, - pub can_print_warnings: bool, + /// Data about code being compiled, gathered during compilation. + pub code_stats: Lock, - next_node_id: Cell + next_node_id: OneThread>, + + /// If `-zfuel=crate=n` is specified, `Some(crate)`. + optimization_fuel_crate: Option, + + /// Tracks fuel info if `-zfuel=crate=n` is specified. + optimization_fuel: Lock, + + // The next two are public because the driver needs to read them. + /// If `-zprint-fuel=crate`, `Some(crate)`. + pub print_fuel_crate: Option, + /// 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, + + /// Metadata about the allocators for the current crate being compiled. + pub has_global_allocator: Once, + + /// Metadata about the panic handlers for the current crate being compiled. + pub has_panic_handler: Once, + + /// Cap lint level specified by a driver specifically. + pub driver_lint_caps: FxHashMap, + + /// `Span`s of trait methods that weren't found to avoid emitting object safety errors + pub trait_methods_not_found: Lock>, + + /// Mapping from ident span to path span for paths that don't exist as written, but that + /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. + pub confused_type_with_std_module: Lock>, +} + +pub struct PerfStats { + /// The accumulated time spent on computing symbol hashes. + pub symbol_hash_time: Lock, + /// The accumulated time spent decoding def path tables from metadata. + pub decode_def_path_tables_time: Lock, + /// Total number of values canonicalized queries constructed. + pub queries_canonicalized: AtomicUsize, + /// Number of times this query is invoked. + pub normalize_ty_after_erasing_regions: AtomicUsize, + /// Number of times this query is invoked. + pub normalize_projection_ty: AtomicUsize, +} + +/// Enum to support dispatch of one-time diagnostics (in `Session.diag_once`). +enum DiagnosticBuilderMethod { + Note, + SpanNote, + SpanSuggestion(String), // suggestion + // Add more variants as needed to support one-time diagnostics. +} + +/// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid +/// emitting the same message more than once. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum DiagnosticMessageId { + ErrorId(u16), // EXXXX error code as integer + LintId(lint::LintId), + StabilityId(u32), // issue number +} + +impl From<&'static lint::Lint> for DiagnosticMessageId { + fn from(lint: &'static lint::Lint) -> Self { + DiagnosticMessageId::LintId(lint::LintId::of(lint)) + } } impl Session { - pub fn span_fatal(&self, sp: Span, msg: &str) -> ! { - if self.opts.treat_err_as_bug { - self.span_bug(sp, msg); - } - panic!(self.diagnostic().span_fatal(sp, msg)) + pub fn local_crate_disambiguator(&self) -> CrateDisambiguator { + *self.crate_disambiguator.get() } - pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> ! { - if self.opts.treat_err_as_bug { - self.span_bug(sp, msg); - } - panic!(self.diagnostic().span_fatal_with_code(sp, msg, code)) + + pub fn struct_span_warn>( + &self, + sp: S, + msg: &str, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_warn(sp, msg) + } + pub fn struct_span_warn_with_code>( + &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>( + &self, + sp: S, + msg: &str, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_err(sp, msg) + } + pub fn struct_span_err_with_code>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_err_with_code(sp, msg, code) + } + // FIXME: This method should be removed (every error should have an associated error 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>( + &self, + sp: S, + msg: &str, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_fatal(sp, msg) + } + pub fn struct_span_fatal_with_code>( + &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>(&self, sp: S, msg: &str) -> ! { + self.diagnostic().span_fatal(sp, msg).raise() + } + pub fn span_fatal_with_code>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> ! { + self.diagnostic() + .span_fatal_with_code(sp, msg, code) + .raise() } pub fn fatal(&self, msg: &str) -> ! { - if self.opts.treat_err_as_bug { - self.bug(msg); - } - self.diagnostic().handler().fatal(msg) + self.diagnostic().fatal(msg).raise() } - pub fn span_err(&self, sp: Span, msg: &str) { - if self.opts.treat_err_as_bug { - self.span_bug(sp, msg); - } - match split_msg_into_multilines(msg) { - Some(msg) => self.diagnostic().span_err(sp, &msg[..]), - None => self.diagnostic().span_err(sp, msg) + pub fn span_err_or_warn>(&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_with_code(&self, sp: Span, msg: &str, code: &str) { - if self.opts.treat_err_as_bug { - self.span_bug(sp, msg); - } - match split_msg_into_multilines(msg) { - Some(msg) => self.diagnostic().span_err_with_code(sp, &msg[..], code), - None => self.diagnostic().span_err_with_code(sp, msg, code) - } + pub fn span_err>(&self, sp: S, msg: &str) { + self.diagnostic().span_err(sp, msg) + } + pub fn span_err_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { + self.diagnostic().span_err_with_code(sp, &msg, code) } pub fn err(&self, msg: &str) { - if self.opts.treat_err_as_bug { - self.bug(msg); - } - self.diagnostic().handler().err(msg) + self.diagnostic().err(msg) } pub fn err_count(&self) -> usize { - self.diagnostic().handler().err_count() + self.diagnostic().err_count() } pub fn has_errors(&self) -> bool { - self.diagnostic().handler().has_errors() + 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().handler().abort_if_errors(); - - let delayed_bug = self.delayed_span_bug.borrow(); - match *delayed_bug { - Some((span, ref errmsg)) => { - self.diagnostic().span_bug(span, errmsg); - }, - _ => {} - } + self.diagnostic().abort_if_errors(); } - pub fn span_warn(&self, sp: Span, msg: &str) { - if self.can_print_warnings { - self.diagnostic().span_warn(sp, msg) + pub fn compile_status(&self) -> Result<(), ErrorReported> { + if self.has_errors() { + self.diagnostic().emit_stashed_diagnostics(); + Err(ErrorReported) + } else { + Ok(()) } } - pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) { - if self.can_print_warnings { - self.diagnostic().span_warn_with_code(sp, msg, code) + // FIXME(matthewjasper) Remove this method, it should never be needed. + pub fn track_errors(&self, f: F) -> Result + 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>(&self, sp: S, msg: &str) { + self.diagnostic().span_warn(sp, msg) + } + pub fn span_warn_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { + self.diagnostic().span_warn_with_code(sp, msg, code) + } pub fn warn(&self, msg: &str) { - if self.can_print_warnings { - self.diagnostic().handler().warn(msg) - } + self.diagnostic().warn(msg) } - pub fn opt_span_warn(&self, opt_sp: Option, msg: &str) { + pub fn opt_span_warn>(&self, opt_sp: Option, msg: &str) { match opt_sp { Some(sp) => self.span_warn(sp, msg), None => self.warn(msg), } } - pub fn span_note(&self, sp: Span, msg: &str) { - self.diagnostic().span_note(sp, msg) - } - pub fn span_end_note(&self, sp: Span, msg: &str) { - self.diagnostic().span_end_note(sp, msg) - } - - /// Prints out a message with a suggested edit of the code. - /// - /// See `diagnostic::RenderSpan::Suggestion` for more information. - pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) { - self.diagnostic().span_suggestion(sp, msg, suggestion) - } - pub fn span_help(&self, sp: Span, msg: &str) { - self.diagnostic().span_help(sp, msg) + /// Delay a span_bug() call until abort_if_errors() + pub fn delay_span_bug>(&self, sp: S, msg: &str) { + self.diagnostic().delay_span_bug(sp, msg) } - pub fn fileline_note(&self, sp: Span, msg: &str) { - self.diagnostic().fileline_note(sp, msg) + pub fn note_without_error(&self, msg: &str) { + self.diagnostic().note_without_error(msg) } - pub fn fileline_help(&self, sp: Span, msg: &str) { - self.diagnostic().fileline_help(sp, msg) + pub fn span_note_without_error>(&self, sp: S, msg: &str) { + self.diagnostic().span_note_without_error(sp, msg) } - pub fn note(&self, msg: &str) { - self.diagnostic().handler().note(msg) + + pub fn reserve_node_ids(&self, count: usize) -> ast::NodeId { + let id = self.next_node_id.get(); + + match id.as_usize().checked_add(count) { + Some(next) => { + self.next_node_id.set(ast::NodeId::from_usize(next)); + } + None => bug!("input too large; ran out of node-IDs!"), + } + + id } - pub fn help(&self, msg: &str) { - self.diagnostic().handler().help(msg) + pub fn next_node_id(&self) -> NodeId { + self.reserve_node_ids(1) } - pub fn opt_span_bug(&self, opt_sp: Option, msg: &str) -> ! { - match opt_sp { - Some(sp) => self.span_bug(sp, msg), - None => self.bug(msg), - } + pub fn diagnostic(&self) -> &errors::Handler { + &self.parse_sess.span_diagnostic } - /// Delay a span_bug() call until abort_if_errors() - pub fn delay_span_bug(&self, sp: Span, msg: &str) { - let mut delayed = self.delayed_span_bug.borrow_mut(); - *delayed = Some((sp, msg.to_string())); - } - pub fn span_bug(&self, sp: Span, msg: &str) -> ! { - self.diagnostic().span_bug(sp, msg) - } - pub fn bug(&self, msg: &str) -> ! { - self.diagnostic().handler().bug(msg) - } - pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! { - self.diagnostic().span_unimpl(sp, msg) - } - pub fn unimpl(&self, msg: &str) -> ! { - self.diagnostic().handler().unimpl(msg) - } - pub fn add_lint(&self, - lint: &'static lint::Lint, - id: ast::NodeId, - sp: Span, - msg: String) { - let lint_id = lint::LintId::of(lint); - let mut lints = self.lints.borrow_mut(); - match lints.get_mut(&id) { - Some(arr) => { arr.push((lint_id, sp, msg)); return; } - None => {} + + /// Analogous to calling methods on the given `DiagnosticBuilder`, but + /// deduplicates on lint ID, span (if any), and message for this `Session` + fn diag_once<'a, 'b>( + &'a self, + diag_builder: &'b mut DiagnosticBuilder<'a>, + method: DiagnosticBuilderMethod, + msg_id: DiagnosticMessageId, + message: &str, + span_maybe: Option, + ) { + 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, + ); + } + } } - lints.insert(id, vec!((lint_id, sp, msg))); } - pub fn next_node_id(&self) -> ast::NodeId { - self.reserve_node_ids(1) + + 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 reserve_node_ids(&self, count: ast::NodeId) -> ast::NodeId { - let id = self.next_node_id.get(); - match id.checked_add(count) { - Some(next) => self.next_node_id.set(next), - None => self.bug("Input too large, ran out of node ids!") - } + 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, + ); + } - id + 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), + ); } - pub fn diagnostic<'a>(&'a self) -> &'a diagnostic::SpanHandler { - &self.parse_sess.span_diagnostic + + pub fn source_map(&self) -> &source_map::SourceMap { + self.parse_sess.source_map() } - pub fn codemap<'a>(&'a self) -> &'a codemap::CodeMap { - self.parse_sess.codemap() + pub fn verbose(&self) -> bool { + self.opts.debugging_opts.verbose } - // This exists to help with refactoring to eliminate impossible - // cases later on - pub fn impossible_case(&self, sp: Span, msg: &str) -> ! { - self.span_bug(sp, - &format!("impossible case reached: {}", msg)); + pub fn time_passes(&self) -> bool { + self.opts.debugging_opts.time_passes || self.opts.debugging_opts.time } - pub fn verbose(&self) -> bool { self.opts.debugging_opts.verbose } - pub fn time_passes(&self) -> bool { self.opts.debugging_opts.time_passes } - pub fn count_llvm_insns(&self) -> bool { - self.opts.debugging_opts.count_llvm_insns + pub fn time_extended(&self) -> bool { + self.opts.debugging_opts.time_passes } - pub fn count_type_sizes(&self) -> bool { - self.opts.debugging_opts.count_type_sizes + 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 trans_stats(&self) -> bool { self.opts.debugging_opts.trans_stats } - 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 no_verify(&self) -> bool { self.opts.debugging_opts.no_verify } - pub fn borrowck_stats(&self) -> bool { self.opts.debugging_opts.borrowck_stats } + 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 + || cfg!(always_verify_llvm_ir) + } + 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 lto(&self) -> bool { - self.opts.cg.lto + pub fn binary_dep_depinfo(&self) -> bool { + self.opts.debugging_opts.binary_dep_depinfo } + + /// 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) -> &feature_gate::Features { + self.features.get() + } + + pub fn init_features(&self, features: feature_gate::Features) { + self.features.set(features); + } + + /// 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.target.options.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.debugging_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.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); + + // Address sanitizer and memory sanitizer use alloca name when reporting an issue. + let more_names = match self.opts.debugging_opts.sanitizer { + Some(Sanitizer::Address) => true, + Some(Sanitizer::Memory) => true, + _ => more_names, + }; + + self.opts.debugging_opts.fewer_names || !more_names + } + pub fn no_landing_pads(&self) -> bool { - self.opts.debugging_opts.no_landing_pads + self.opts.debugging_opts.no_landing_pads || self.panic_strategy() == PanicStrategy::Abort } pub fn unstable_options(&self) -> bool { self.opts.debugging_opts.unstable_options } - pub fn print_enum_sizes(&self) -> bool { - self.opts.debugging_opts.print_enum_sizes + 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 nonzeroing_move_hints(&self) -> bool { - self.opts.debugging_opts.enable_nonzeroing_move_hints + + pub fn crt_static(&self) -> bool { + // If the target does not opt in to crt-static support, use its default. + if self.target.target.options.crt_static_respected { + self.crt_static_feature() + } else { + self.target.target.options.crt_static_default + } + } + + pub fn crt_static_feature(&self) -> bool { + 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 the target we're compiling for requests a static crt by default, + // then see if the `-crt-static` feature was passed to disable that. + // Otherwise if we don't have a static crt by default then see if the + // `+crt-static` feature was passed. + if self.target.target.options.crt_static_default { + !found_negative + } else { + found_positive + } } - pub fn sysroot<'a>(&'a self) -> &'a Path { - match self.opts.maybe_sysroot { - Some (ref sysroot) => sysroot, - None => self.default_sysroot.as_ref() - .expect("missing sysroot and default_sysroot in Session") + + pub fn must_not_eliminate_frame_pointers(&self) -> bool { + // "mcount" function relies on stack pointer. + // See . + 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 target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch { - filesearch::FileSearch::new(self.sysroot(), - &self.opts.target_triple, - &self.opts.search_paths, - kind) + + /// Returns the symbol name for the registrar function, + /// given the crate `Svh` and the function `DefIndex`. + 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, + // `target_tlib_path == None` means it's the same as `host_tlib_path`. + self.target_tlib_path.as_ref().unwrap_or(&self.host_tlib_path), + kind, + ) } - pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch { + pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { filesearch::FileSearch::new( - self.sysroot(), + &self.sysroot, config::host_triple(), &self.opts.search_paths, - kind) + &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 { + bug!( + "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 { + bug!( + "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, + _ => bug!( + "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 => bug!( + "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> { + if self.opts.incremental.is_some() { + Some(self.incr_comp_session_dir()) + } else { + None + } + } + + 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 time spent decoding DefPath tables: {}", + duration_to_secs_str(*self.perf_stats.decode_def_path_tables_time.lock()) + ); + println!("Total queries canonicalized: {}", + self.perf_stats.queries_canonicalized.load(Ordering::Relaxed)); + println!("normalize_ty_after_erasing_regions: {}", + self.perf_stats.normalize_ty_after_erasing_regions.load(Ordering::Relaxed)); + println!("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 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 { + eprintln!("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 + } + + /// Returns the number of query threads that should be used for this + /// compilation + pub fn threads(&self) -> usize { + self.opts.debugging_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.target.options.default_codegen_units { + return n as usize; + } + + // 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.debugging_opts.teach && self.diagnostic().must_teach(code) + } + + pub fn rust_2015(&self) -> bool { + self.opts.edition == Edition::Edition2015 + } + + /// Are we allowed to use features from the Rust 2018 edition? + pub fn rust_2018(&self) -> bool { + self.opts.edition >= Edition::Edition2018 + } + + pub fn edition(&self) -> Edition { + self.opts.edition + } + + /// 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.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); + + // 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) + } +} + +pub fn build_session( + sopts: config::Options, + local_crate_source_file: Option, + registry: errors::registry::Registry, +) -> Session { + let file_path_mapping = sopts.file_path_mapping(); + + build_session_with_source_map( + sopts, + local_crate_source_file, + registry, + Lrc::new(source_map::SourceMap::new(file_path_mapping)), + DiagnosticOutput::Default, + Default::default(), + ) +} + +fn default_emitter( + sopts: &config::Options, + registry: errors::registry::Registry, + source_map: &Lrc, + emitter_dest: Option>, +) -> Box { + let external_macro_backtrace = sopts.debugging_opts.external_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.clone()), + short, + external_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.clone()), + short, + sopts.debugging_opts.teach, + sopts.debugging_opts.terminal_width, + external_macro_backtrace, + ), + Some(dst) => EmitterWriter::new( + dst, + Some(source_map.clone()), + short, + false, // no teach messages when writing to a buffer + false, // no colors when writing to a buffer + None, // no terminal width + external_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.clone(), + pretty, + json_rendered, + external_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.clone(), + pretty, + json_rendered, + external_macro_backtrace, + ).ui_testing(sopts.debugging_opts.ui_testing), + ), } } -fn split_msg_into_multilines(msg: &str) -> Option { - // Conditions for enabling multi-line errors: - if !msg.contains("mismatched types") && - !msg.contains("type mismatch resolving") && - !msg.contains("if and else have incompatible types") && - !msg.contains("if may be missing an else clause") && - !msg.contains("match arms have incompatible types") && - !msg.contains("structure constructor specifies a structure of type") && - !msg.contains("has an incompatible type for trait") { - return None - } - let first = msg.match_indices("expected").filter(|s| { - s.0 > 0 && (msg.char_at_reverse(s.0) == ' ' || - msg.char_at_reverse(s.0) == '(') - }).map(|(a, b)| (a - 1, b)); - let second = msg.match_indices("found").filter(|s| { - msg.char_at_reverse(s.0) == ' ' - }).map(|(a, b)| (a - 1, b)); - - let mut new_msg = String::new(); - let mut head = 0; - - // Insert `\n` before expected and found. - for (pos1, pos2) in first.zip(second) { - new_msg = new_msg + - // A `(` may be preceded by a space and it should be trimmed - msg[head..pos1.0].trim_right() + // prefix - "\n" + // insert before first - &msg[pos1.0..pos1.1] + // insert what first matched - &msg[pos1.1..pos2.0] + // between matches - "\n " + // insert before second - // 123 - // `expected` is 3 char longer than `found`. To align the types, - // `found` gets 3 spaces prepended. - &msg[pos2.0..pos2.1]; // insert what second matched - - head = pos2.1; - } - - let mut tail = &msg[head..]; - let third = tail.find("(values differ") - .or(tail.find("(lifetime")) - .or(tail.find("(cyclic type of infinite size")); - // Insert `\n` before any remaining messages which match. - if let Some(pos) = third { - // The end of the message may just be wrapped in `()` without - // `expected`/`found`. Push this also to a new line and add the - // final tail after. - new_msg = new_msg + - // `(` is usually preceded by a space and should be trimmed. - tail[..pos].trim_right() + // prefix - "\n" + // insert before paren - &tail[pos..]; // append the tail - - tail = ""; - } - - new_msg.push_str(tail); - return Some(new_msg); +pub enum DiagnosticOutput { + Default, + Raw(Box) } -pub fn build_session(sopts: config::Options, - local_crate_source_file: Option, - registry: diagnostics::registry::Registry) - -> Session { +pub fn build_session_with_source_map( + sopts: config::Options, + local_crate_source_file: Option, + registry: errors::registry::Registry, + source_map: Lrc, + diagnostics_output: DiagnosticOutput, + lint_caps: FxHashMap, +) -> 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 can_print_warnings = sopts.lint_opts + let warnings_allow = sopts + .lint_opts .iter() .filter(|&&(ref key, _)| *key == "warnings") - .map(|&(_, ref level)| *level != lint::Allow) + .map(|&(_, ref level)| *level == lint::Allow) .last() - .unwrap_or(true); + .unwrap_or(false); + let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow); - let codemap = codemap::CodeMap::new(); - let diagnostic_handler = - diagnostic::Handler::new(sopts.color, Some(registry), can_print_warnings); - let span_diagnostic_handler = - diagnostic::SpanHandler::new(diagnostic_handler, codemap); + let can_emit_warnings = !(warnings_allow || cap_lints_allow); - build_session_(sopts, local_crate_source_file, span_diagnostic_handler) -} + let treat_err_as_bug = sopts.debugging_opts.treat_err_as_bug; + let dont_buffer_diagnostics = sopts.debugging_opts.dont_buffer_diagnostics; + let report_delayed_bugs = sopts.debugging_opts.report_delayed_bugs; -pub fn build_session_(sopts: config::Options, - local_crate_source_file: Option, - span_diagnostic: diagnostic::SpanHandler) - -> Session { - let host = match Target::search(config::host_triple()) { - Ok(t) => t, - Err(e) => { - span_diagnostic.handler() - .fatal(&format!("Error loading host specification: {}", e)); - } + let external_macro_backtrace = sopts.debugging_opts.external_macro_backtrace; + + let emitter = match diagnostics_output { + DiagnosticOutput::Default => default_emitter(&sopts, registry, &source_map, None), + DiagnosticOutput::Raw(write) => { + default_emitter(&sopts, registry, &source_map, Some(write)) + } }; + + let diagnostic_handler = errors::Handler::with_emitter_and_flags( + emitter, + errors::HandlerFlags { + can_emit_warnings, + treat_err_as_bug, + report_delayed_bugs, + dont_buffer_diagnostics, + external_macro_backtrace, + ..Default::default() + }, + ); + + build_session_(sopts, local_crate_source_file, diagnostic_handler, source_map, lint_caps) +} + +fn build_session_( + sopts: config::Options, + local_crate_source_file: Option, + span_diagnostic: errors::Handler, + source_map: Lrc, + driver_lint_caps: FxHashMap, +) -> Session { + 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) => { + crate::ty::query::QueryName::register_with_profiler(&profiler); + Some(Arc::new(profiler)) + }, + Err(e) => { + early_warn(sopts.error_format, &format!("failed to create profiler: {}", e)); + None + } + } + } + else { None }; + + let host_triple = TargetTriple::from_triple(config::host_triple()); + let host = Target::search(&host_triple).unwrap_or_else(|e| + span_diagnostic + .fatal(&format!("Error loading host specification: {}", e)) + .raise() + ); let target_cfg = config::build_target_config(&sopts, &span_diagnostic); - let p_s = parse::ParseSess::with_span_handler(span_diagnostic); - let default_sysroot = match sopts.maybe_sysroot { - Some(_) => None, - None => Some(filesearch::get_or_default_sysroot()) + + 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(), }; - // Make the path absolute, if necessary - let local_crate_source_file = local_crate_source_file.map(|path| - if path.is_absolute() { - path.clone() - } else { - env::current_dir().unwrap().join(&path) - } + 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 can_print_warnings = sopts.lint_opts - .iter() - .filter(|&&(ref key, _)| *key == "warnings") - .map(|&(_, ref level)| *level != lint::Allow) - .last() - .unwrap_or(true); + let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph { + CguReuseTracker::new() + } else { + CguReuseTracker::new_disabled() + }; let sess = Session { target: target_cfg, - host: host, + host, opts: sopts, - cstore: CStore::new(token::get_ident_interner()), - parse_sess: p_s, - // For a library crate, this is always none - entry_fn: RefCell::new(None), - entry_type: Cell::new(None), - plugin_registrar_fn: Cell::new(None), - default_sysroot: default_sysroot, - local_crate_source_file: local_crate_source_file, - working_dir: env::current_dir().unwrap(), - lint_store: RefCell::new(lint::LintStore::new()), - lints: RefCell::new(NodeMap()), - plugin_llvm_passes: RefCell::new(Vec::new()), - plugin_attributes: RefCell::new(Vec::new()), - crate_types: RefCell::new(Vec::new()), - crate_metadata: RefCell::new(Vec::new()), - delayed_span_bug: RefCell::new(None), - features: RefCell::new(feature_gate::Features::new()), - recursion_limit: Cell::new(64), - can_print_warnings: can_print_warnings, - next_node_id: Cell::new(1) + host_tlib_path, + target_tlib_path, + parse_sess, + sysroot, + local_crate_source_file, + working_dir, + one_time_diagnostics: Default::default(), + plugin_llvm_passes: OneThread::new(RefCell::new(Vec::new())), + plugin_attributes: Lock::new(Vec::new()), + crate_types: Once::new(), + crate_disambiguator: Once::new(), + features: Once::new(), + recursion_limit: Once::new(), + type_length_limit: Once::new(), + const_eval_stack_frame_limit: 100, + next_node_id: OneThread::new(Cell::new(NodeId::from_u32(1))), + allocator_kind: Once::new(), + injected_panic_runtime: Once::new(), + imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())), + incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), + cgu_reuse_tracker, + prof: SelfProfilerRef::new(self_profiler), + perf_stats: PerfStats { + symbol_hash_time: Lock::new(Duration::from_secs(0)), + decode_def_path_tables_time: Lock::new(Duration::from_secs(0)), + queries_canonicalized: AtomicUsize::new(0), + normalize_ty_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(), + has_global_allocator: Once::new(), + has_panic_handler: Once::new(), + driver_lint_caps, + trait_methods_not_found: Lock::new(Default::default()), + confused_type_with_std_module: Lock::new(Default::default()), }; + validate_commandline_args_with_session_available(&sess); + sess } -// Seems out of place, but it uses session, so I'm putting it here -pub fn expect(sess: &Session, opt: Option, msg: M) -> T where - M: FnOnce() -> String, -{ - diagnostic::expect(sess.diagnostic(), opt, msg) +// If it is useful to have a Session available already for validating a +// commandline argument, you can do so here. +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 the + // MSVC linker 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 MSVC + // 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.target.options.is_like_msvc { + sess.err("Linker plugin based LTO is not supported together with \ + `-C prefer-dynamic` when targeting MSVC"); + } + + // 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())); + } + } + + // PGO does not work reliably with panic=unwind on Windows. Let's make it + // an error to combine the two for now. It always runs into an assertions + // if LLVM is built with assertions, but without assertions it sometimes + // does not crash and will probably generate a corrupted binary. + // We should only display this error if we're actually going to run PGO. + // If we're just supposed to print out some data, don't show the error (#61002). + 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 https://github.com/rust-lang/rust/issues/61002 for details."); + } } -pub fn early_error(msg: &str) -> ! { - let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); - emitter.emit(None, msg, None, diagnostic::Fatal); - panic!(diagnostic::FatalError); +/// Hash value constructed out of all the `-C metadata` arguments passed to the +/// compiler. Together with the crate-name forms a unique global identifier for +/// the crate. +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, RustcEncodable, RustcDecodable)] +pub struct CrateDisambiguator(Fingerprint); + +impl CrateDisambiguator { + pub fn to_fingerprint(self) -> Fingerprint { + self.0 + } } -pub fn early_warn(msg: &str) { - let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); - emitter.emit(None, msg, None, diagnostic::Warning); +impl fmt::Display for CrateDisambiguator { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let (a, b) = self.0.as_value(); + let as_u128 = a as u128 | ((b as u128) << 64); + f.write_str(&base_n::encode(as_u128, base_n::CASE_INSENSITIVE)) + } } + +impl From for CrateDisambiguator { + fn from(fingerprint: Fingerprint) -> CrateDisambiguator { + CrateDisambiguator(fingerprint) + } +} + +impl_stable_hash_via_hash!(CrateDisambiguator); + +/// 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 }, +} + +pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { + let emitter: Box = 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, false)), + }; + let handler = errors::Handler::with_emitter(true, None, emitter); + handler.struct_fatal(msg).emit(); + errors::FatalError.raise(); +} + +pub fn early_warn(output: config::ErrorOutputType, msg: &str) { + let emitter: Box = 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, false)), + }; + let handler = errors::Handler::with_emitter(true, None, emitter); + handler.struct_warn(msg).emit(); +} + +pub type CompileResult = Result<(), ErrorReported>;