about summary refs log tree commit diff
path: root/compiler/rustc_interface/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_interface/src')
-rw-r--r--compiler/rustc_interface/src/callbacks.rs13
-rw-r--r--compiler/rustc_interface/src/errors.rs114
-rw-r--r--compiler/rustc_interface/src/interface.rs308
-rw-r--r--compiler/rustc_interface/src/lib.rs16
-rw-r--r--compiler/rustc_interface/src/passes.rs904
-rw-r--r--compiler/rustc_interface/src/proc_macro_decls.rs19
-rw-r--r--compiler/rustc_interface/src/queries.rs307
-rw-r--r--compiler/rustc_interface/src/tests.rs229
-rw-r--r--compiler/rustc_interface/src/util.rs442
9 files changed, 1174 insertions, 1178 deletions
diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs
index 76442de69d3..bc6d7c20997 100644
--- a/compiler/rustc_interface/src/callbacks.rs
+++ b/compiler/rustc_interface/src/callbacks.rs
@@ -10,6 +10,7 @@
 //! origin crate when the `TyCtxt` is not present in TLS.
 
 use rustc_errors::{Diagnostic, TRACK_DIAGNOSTICS};
+use rustc_middle::dep_graph::TaskDepsRef;
 use rustc_middle::ty::tls;
 use std::fmt;
 
@@ -26,14 +27,22 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) {
 /// This is a callback from `rustc_ast` as it cannot access the implicit state
 /// in `rustc_middle` otherwise. It is used when diagnostic messages are
 /// emitted and stores them in the current query, if there is one.
-fn track_diagnostic(diagnostic: &Diagnostic) {
+fn track_diagnostic(diagnostic: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) {
     tls::with_context_opt(|icx| {
         if let Some(icx) = icx {
             if let Some(diagnostics) = icx.diagnostics {
                 let mut diagnostics = diagnostics.lock();
                 diagnostics.extend(Some(diagnostic.clone()));
+                std::mem::drop(diagnostics);
             }
+
+            // Diagnostics are tracked, we can ignore the dependency.
+            let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() };
+            return tls::enter_context(&icx, move || (*f)(diagnostic));
         }
+
+        // In any other case, invoke diagnostics anyway.
+        (*f)(diagnostic);
     })
 }
 
@@ -55,5 +64,5 @@ fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) ->
 pub fn setup_callbacks() {
     rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_)));
     rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
-    TRACK_DIAGNOSTICS.swap(&(track_diagnostic as fn(&_)));
+    TRACK_DIAGNOSTICS.swap(&(track_diagnostic as _));
 }
diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs
new file mode 100644
index 00000000000..a9ab2720d89
--- /dev/null
+++ b/compiler/rustc_interface/src/errors.rs
@@ -0,0 +1,114 @@
+use rustc_macros::Diagnostic;
+use rustc_session::config::CrateType;
+use rustc_span::{Span, Symbol};
+use rustc_target::spec::TargetTriple;
+
+use std::io;
+use std::path::Path;
+
+#[derive(Diagnostic)]
+#[diag(interface_ferris_identifier)]
+pub struct FerrisIdentifier {
+    #[primary_span]
+    pub spans: Vec<Span>,
+    #[suggestion(code = "ferris", applicability = "maybe-incorrect")]
+    pub first_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_emoji_identifier)]
+pub struct EmojiIdentifier {
+    #[primary_span]
+    pub spans: Vec<Span>,
+    pub ident: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_mixed_bin_crate)]
+pub struct MixedBinCrate;
+
+#[derive(Diagnostic)]
+#[diag(interface_mixed_proc_macro_crate)]
+pub struct MixedProcMacroCrate;
+
+#[derive(Diagnostic)]
+#[diag(interface_error_writing_dependencies)]
+pub struct ErrorWritingDependencies<'a> {
+    pub path: &'a Path,
+    pub error: io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_input_file_would_be_overwritten)]
+pub struct InputFileWouldBeOverWritten<'a> {
+    pub path: &'a Path,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_generated_file_conflicts_with_directory)]
+pub struct GeneratedFileConflictsWithDirectory<'a> {
+    pub input_path: &'a Path,
+    pub dir_path: &'a Path,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_temps_dir_error)]
+pub struct TempsDirError;
+
+#[derive(Diagnostic)]
+#[diag(interface_out_dir_error)]
+pub struct OutDirError;
+
+#[derive(Diagnostic)]
+#[diag(interface_cant_emit_mir)]
+pub struct CantEmitMIR {
+    pub error: io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_rustc_error_fatal)]
+pub struct RustcErrorFatal {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_rustc_error_unexpected_annotation)]
+pub struct RustcErrorUnexpectedAnnotation {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_failed_writing_file)]
+pub struct FailedWritingFile<'a> {
+    pub path: &'a Path,
+    pub error: io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_proc_macro_crate_panic_abort)]
+pub struct ProcMacroCratePanicAbort;
+
+#[derive(Diagnostic)]
+#[diag(interface_unsupported_crate_type_for_target)]
+pub struct UnsupportedCrateTypeForTarget<'a> {
+    pub crate_type: CrateType,
+    pub target_triple: &'a TargetTriple,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_multiple_output_types_adaption)]
+pub struct MultipleOutputTypesAdaption;
+
+#[derive(Diagnostic)]
+#[diag(interface_ignoring_extra_filename)]
+pub struct IgnoringExtraFilename;
+
+#[derive(Diagnostic)]
+#[diag(interface_ignoring_out_dir)]
+pub struct IgnoringOutDir;
+
+#[derive(Diagnostic)]
+#[diag(interface_multiple_output_types_to_stdout)]
+pub struct MultipleOutputTypesToStdout;
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index e7563933c88..5b417e008cf 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -1,23 +1,24 @@
-pub use crate::passes::BoxedResolver;
 use crate::util;
 
 use rustc_ast::token;
 use rustc_ast::{self as ast, LitKind, MetaItemKind};
 use rustc_codegen_ssa::traits::CodegenBackend;
+use rustc_data_structures::defer;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::Lrc;
-use rustc_data_structures::OnDrop;
 use rustc_errors::registry::Registry;
 use rustc_errors::{ErrorGuaranteed, Handler};
 use rustc_lint::LintStore;
-use rustc_middle::ty;
+use rustc_middle::query::{ExternProviders, Providers};
+use rustc_middle::{bug, ty};
 use rustc_parse::maybe_new_parser_from_source_str;
 use rustc_query_impl::QueryCtxt;
-use rustc_session::config::{self, CheckCfg, ErrorOutputType, Input, OutputFilenames};
-use rustc_session::early_error;
-use rustc_session::lint;
+use rustc_query_system::query::print_query_stack;
+use rustc_session::config::{self, CheckCfg, ExpectedValues, Input, OutFileName, OutputFilenames};
 use rustc_session::parse::{CrateConfig, ParseSess};
-use rustc_session::{DiagnosticOutput, Session};
+use rustc_session::CompilerIO;
+use rustc_session::Session;
+use rustc_session::{lint, EarlyErrorHandler};
 use rustc_span::source_map::{FileLoader, FileName};
 use rustc_span::symbol::sym;
 use std::path::PathBuf;
@@ -25,42 +26,27 @@ use std::result;
 
 pub type Result<T> = result::Result<T, ErrorGuaranteed>;
 
-/// Represents a compiler session.
+/// Represents a compiler session. Note that every `Compiler` contains a
+/// `Session`, but `Compiler` also contains some things that cannot be in
+/// `Session`, due to `Session` being in a crate that has many fewer
+/// dependencies than this crate.
 ///
 /// Can be used to run `rustc_interface` queries.
 /// Created by passing [`Config`] to [`run_compiler`].
 pub struct Compiler {
     pub(crate) sess: Lrc<Session>,
-    codegen_backend: Lrc<Box<dyn CodegenBackend>>,
-    pub(crate) input: Input,
-    pub(crate) input_path: Option<PathBuf>,
-    pub(crate) output_dir: Option<PathBuf>,
-    pub(crate) output_file: Option<PathBuf>,
-    pub(crate) temps_dir: Option<PathBuf>,
+    codegen_backend: Lrc<dyn CodegenBackend>,
     pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
-    pub(crate) override_queries:
-        Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::ExternProviders)>,
+    pub(crate) override_queries: Option<fn(&Session, &mut Providers, &mut ExternProviders)>,
 }
 
 impl Compiler {
     pub fn session(&self) -> &Lrc<Session> {
         &self.sess
     }
-    pub fn codegen_backend(&self) -> &Lrc<Box<dyn CodegenBackend>> {
+    pub fn codegen_backend(&self) -> &Lrc<dyn CodegenBackend> {
         &self.codegen_backend
     }
-    pub fn input(&self) -> &Input {
-        &self.input
-    }
-    pub fn output_dir(&self) -> &Option<PathBuf> {
-        &self.output_dir
-    }
-    pub fn output_file(&self) -> &Option<PathBuf> {
-        &self.output_file
-    }
-    pub fn temps_dir(&self) -> &Option<PathBuf> {
-        &self.temps_dir
-    }
     pub fn register_lints(&self) -> &Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>> {
         &self.register_lints
     }
@@ -69,35 +55,30 @@ impl Compiler {
         sess: &Session,
         attrs: &[ast::Attribute],
     ) -> OutputFilenames {
-        util::build_output_filenames(
-            &self.input,
-            &self.output_dir,
-            &self.output_file,
-            &self.temps_dir,
-            attrs,
-            sess,
-        )
+        util::build_output_filenames(attrs, sess)
     }
 }
 
 /// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`.
-pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> {
+pub fn parse_cfgspecs(
+    handler: &EarlyErrorHandler,
+    cfgspecs: Vec<String>,
+) -> FxHashSet<(String, Option<String>)> {
     rustc_span::create_default_session_if_not_set_then(move |_| {
         let cfg = cfgspecs
             .into_iter()
             .map(|s| {
                 let sess = ParseSess::with_silent_emitter(Some(format!(
-                    "this error occurred on the command line: `--cfg={}`",
-                    s
+                    "this error occurred on the command line: `--cfg={s}`"
                 )));
                 let filename = FileName::cfg_spec_source_code(&s);
 
                 macro_rules! error {
                     ($reason: expr) => {
-                        early_error(
-                            ErrorOutputType::default(),
-                            &format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s),
-                        );
+                        handler.early_error(format!(
+                            concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
+                            s
+                        ));
                     };
                 }
 
@@ -141,59 +122,79 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
 }
 
 /// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
-pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
+pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> CheckCfg {
     rustc_span::create_default_session_if_not_set_then(move |_| {
-        let mut cfg = CheckCfg::default();
+        let mut check_cfg = CheckCfg::default();
 
-        'specs: for s in specs {
+        for s in specs {
             let sess = ParseSess::with_silent_emitter(Some(format!(
-                "this error occurred on the command line: `--check-cfg={}`",
-                s
+                "this error occurred on the command line: `--check-cfg={s}`"
             )));
             let filename = FileName::cfg_spec_source_code(&s);
 
             macro_rules! error {
                 ($reason: expr) => {
-                    early_error(
-                        ErrorOutputType::default(),
-                        &format!(
-                            concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
-                            s
-                        ),
-                    );
+                    handler.early_error(format!(
+                        concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
+                        s
+                    ))
                 };
             }
 
+            let expected_error = || {
+                error!(
+                    "expected `names(name1, name2, ... nameN)` or \
+                        `values(name, \"value1\", \"value2\", ... \"valueN\")`"
+                )
+            };
+
             match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
                 Ok(mut parser) => match parser.parse_meta_item() {
                     Ok(meta_item) if parser.token == token::Eof => {
                         if let Some(args) = meta_item.meta_item_list() {
                             if meta_item.has_name(sym::names) {
-                                let names_valid =
-                                    cfg.names_valid.get_or_insert_with(|| FxHashSet::default());
+                                check_cfg.exhaustive_names = true;
                                 for arg in args {
                                     if arg.is_word() && arg.ident().is_some() {
                                         let ident = arg.ident().expect("multi-segment cfg key");
-                                        names_valid.insert(ident.name.to_string());
+                                        check_cfg
+                                            .expecteds
+                                            .entry(ident.name.to_string())
+                                            .or_insert(ExpectedValues::Any);
                                     } else {
-                                        error!("`names()` arguments must be simple identifers");
+                                        error!("`names()` arguments must be simple identifiers");
                                     }
                                 }
-                                continue 'specs;
                             } else if meta_item.has_name(sym::values) {
                                 if let Some((name, values)) = args.split_first() {
                                     if name.is_word() && name.ident().is_some() {
                                         let ident = name.ident().expect("multi-segment cfg key");
-                                        let ident_values = cfg
-                                            .values_valid
+                                        let expected_values = check_cfg
+                                            .expecteds
                                             .entry(ident.name.to_string())
-                                            .or_insert_with(|| FxHashSet::default());
+                                            .and_modify(|expected_values| match expected_values {
+                                                ExpectedValues::Some(_) => {}
+                                                ExpectedValues::Any => {
+                                                    // handle the case where names(...) was done
+                                                    // before values by changing to a list
+                                                    *expected_values =
+                                                        ExpectedValues::Some(FxHashSet::default());
+                                                }
+                                            })
+                                            .or_insert_with(|| {
+                                                ExpectedValues::Some(FxHashSet::default())
+                                            });
+
+                                        let ExpectedValues::Some(expected_values) = expected_values
+                                        else {
+                                            bug!("`expected_values` should be a list a values")
+                                        };
 
                                         for val in values {
                                             if let Some(LitKind::Str(s, _)) =
-                                                val.literal().map(|lit| &lit.kind)
+                                                val.lit().map(|lit| &lit.kind)
                                             {
-                                                ident_values.insert(s.to_string());
+                                                expected_values.insert(Some(s.to_string()));
                                             } else {
                                                 error!(
                                                     "`values()` arguments must be string literals"
@@ -201,35 +202,40 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
                                             }
                                         }
 
-                                        continue 'specs;
+                                        if values.is_empty() {
+                                            expected_values.insert(None);
+                                        }
                                     } else {
                                         error!(
-                                            "`values()` first argument must be a simple identifer"
+                                            "`values()` first argument must be a simple identifier"
                                         );
                                     }
                                 } else if args.is_empty() {
-                                    cfg.well_known_values = true;
-                                    continue 'specs;
+                                    check_cfg.exhaustive_values = true;
+                                } else {
+                                    expected_error();
                                 }
+                            } else {
+                                expected_error();
                             }
+                        } else {
+                            expected_error();
                         }
                     }
-                    Ok(..) => {}
-                    Err(err) => err.cancel(),
+                    Ok(..) => expected_error(),
+                    Err(err) => {
+                        err.cancel();
+                        expected_error();
+                    }
                 },
-                Err(errs) => drop(errs),
+                Err(errs) => {
+                    drop(errs);
+                    expected_error();
+                }
             }
-
-            error!(
-                "expected `names(name1, name2, ... nameN)` or \
-                `values(name, \"value1\", \"value2\", ... \"valueN\")`"
-            );
         }
 
-        if let Some(names_valid) = &mut cfg.names_valid {
-            names_valid.extend(cfg.values_valid.keys().cloned());
-        }
-        cfg
+        check_cfg
     })
 }
 
@@ -243,11 +249,11 @@ pub struct Config {
     pub crate_check_cfg: CheckCfg,
 
     pub input: Input,
-    pub input_path: Option<PathBuf>,
     pub output_dir: Option<PathBuf>,
-    pub output_file: Option<PathBuf>,
+    pub output_file: Option<OutFileName>,
+    pub ice_file: Option<PathBuf>,
     pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
-    pub diagnostic_output: DiagnosticOutput,
+    pub locale_resources: &'static [&'static str],
 
     pub lint_caps: FxHashMap<lint::LintId, lint::Level>,
 
@@ -265,8 +271,7 @@ pub struct Config {
     /// the list of queries.
     ///
     /// The second parameter is local providers and the third parameter is external providers.
-    pub override_queries:
-        Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::ExternProviders)>,
+    pub override_queries: Option<fn(&Session, &mut Providers, &mut ExternProviders)>,
 
     /// This is a callback from the driver that is called to create a codegen backend.
     pub make_codegen_backend:
@@ -276,69 +281,78 @@ pub struct Config {
     pub registry: Registry,
 }
 
-pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R) -> R {
-    crate::callbacks::setup_callbacks();
-
-    let registry = &config.registry;
-    let (mut sess, codegen_backend) = util::create_session(
-        config.opts,
-        config.crate_cfg,
-        config.crate_check_cfg,
-        config.diagnostic_output,
-        config.file_loader,
-        config.input_path.clone(),
-        config.lint_caps,
-        config.make_codegen_backend,
-        registry.clone(),
-    );
-
-    if let Some(parse_sess_created) = config.parse_sess_created {
-        parse_sess_created(
-            &mut Lrc::get_mut(&mut sess)
-                .expect("create_session() should never share the returned session")
-                .parse_sess,
-        );
-    }
+// JUSTIFICATION: before session exists, only config
+#[allow(rustc::bad_opt_access)]
+pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
+    trace!("run_compiler");
 
-    let temps_dir = sess.opts.debugging_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o));
-
-    let compiler = Compiler {
-        sess,
-        codegen_backend,
-        input: config.input,
-        input_path: config.input_path,
-        output_dir: config.output_dir,
-        output_file: config.output_file,
-        temps_dir,
-        register_lints: config.register_lints,
-        override_queries: config.override_queries,
-    };
-
-    rustc_span::with_source_map(compiler.sess.parse_sess.clone_source_map(), move || {
-        let r = {
-            let _sess_abort_error = OnDrop(|| {
-                compiler.sess.finish_diagnostics(registry);
-            });
-
-            f(&compiler)
-        };
-
-        let prof = compiler.sess.prof.clone();
-        prof.generic_activity("drop_compiler").run(move || drop(compiler));
-        r
-    })
-}
+    // Set parallel mode before thread pool creation, which will create `Lock`s.
+    rustc_data_structures::sync::set_dyn_thread_safe_mode(config.opts.unstable_opts.threads > 1);
 
-pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
-    tracing::trace!("run_compiler");
     util::run_in_thread_pool_with_globals(
         config.opts.edition,
-        config.opts.debugging_opts.threads,
-        || create_compiler_and_run(config, f),
+        config.opts.unstable_opts.threads,
+        || {
+            crate::callbacks::setup_callbacks();
+
+            let registry = &config.registry;
+
+            let handler = EarlyErrorHandler::new(config.opts.error_format);
+
+            let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
+            let (mut sess, codegen_backend) = util::create_session(
+                &handler,
+                config.opts,
+                config.crate_cfg,
+                config.crate_check_cfg,
+                config.locale_resources,
+                config.file_loader,
+                CompilerIO {
+                    input: config.input,
+                    output_dir: config.output_dir,
+                    output_file: config.output_file,
+                    temps_dir,
+                },
+                config.lint_caps,
+                config.make_codegen_backend,
+                registry.clone(),
+                config.ice_file,
+            );
+
+            if let Some(parse_sess_created) = config.parse_sess_created {
+                parse_sess_created(&mut sess.parse_sess);
+            }
+
+            let compiler = Compiler {
+                sess: Lrc::new(sess),
+                codegen_backend: Lrc::from(codegen_backend),
+                register_lints: config.register_lints,
+                override_queries: config.override_queries,
+            };
+
+            rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || {
+                let r = {
+                    let _sess_abort_error = defer(|| {
+                        compiler.sess.finish_diagnostics(registry);
+                    });
+
+                    f(&compiler)
+                };
+
+                let prof = compiler.sess.prof.clone();
+
+                prof.generic_activity("drop_compiler").run(move || drop(compiler));
+                r
+            })
+        },
     )
 }
 
-pub fn try_print_query_stack(handler: &Handler, num_frames: Option<usize>) {
+pub fn try_print_query_stack(
+    handler: &Handler,
+    num_frames: Option<usize>,
+    file: Option<std::fs::File>,
+) {
     eprintln!("query stack during panic:");
 
     // Be careful relying on global state here: this code is called from
@@ -346,7 +360,13 @@ pub fn try_print_query_stack(handler: &Handler, num_frames: Option<usize>) {
     // state if it was responsible for triggering the panic.
     let i = ty::tls::with_context_opt(|icx| {
         if let Some(icx) = icx {
-            QueryCtxt::from_tcx(icx.tcx).try_print_query_stack(icx.query, handler, num_frames)
+            ty::print::with_no_queries!(print_query_stack(
+                QueryCtxt::new(icx.tcx),
+                icx.query,
+                handler,
+                num_frames,
+                file,
+            ))
         } else {
             0
         }
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index d443057eb79..51bd8381e93 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -1,12 +1,22 @@
 #![feature(box_patterns)]
-#![feature(let_else)]
+#![feature(decl_macro)]
 #![feature(internal_output_capture)]
 #![feature(thread_spawn_unchecked)]
-#![feature(once_cell)]
+#![feature(lazy_cell)]
+#![feature(try_blocks)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
+
+#[macro_use]
+extern crate tracing;
+
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_fluent_macro::fluent_messages;
 
 mod callbacks;
+mod errors;
 pub mod interface;
 mod passes;
 mod proc_macro_decls;
@@ -20,3 +30,5 @@ pub use queries::Queries;
 
 #[cfg(test)]
 mod tests;
+
+fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 502afa493fe..6b3facd041c 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -1,75 +1,66 @@
+use crate::errors;
 use crate::interface::{Compiler, Result};
 use crate::proc_macro_decls;
 use crate::util;
 
-use ast::CRATE_NODE_ID;
 use rustc_ast::{self as ast, visit};
 use rustc_borrowck as mir_borrowck;
-use rustc_codegen_ssa::back::link::emit_metadata;
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_data_structures::parallel;
+use rustc_data_structures::steal::Steal;
 use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
-use rustc_data_structures::temp_dir::MaybeTempDir;
-use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, PResult};
-use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand};
+use rustc_errors::PResult;
+use rustc_expand::base::{ExtCtxt, LintStoreExpand};
+use rustc_fs_util::try_canonicalize;
 use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE};
-use rustc_hir::definitions::Definitions;
-use rustc_hir::Crate;
-use rustc_lint::{EarlyCheckNode, LintStore};
+use rustc_lint::{unerased_lint_store, BufferedEarlyLint, EarlyCheckNode, LintStore};
 use rustc_metadata::creader::CStore;
-use rustc_metadata::{encode_metadata, EncodedMetadata};
 use rustc_middle::arena::Arena;
 use rustc_middle::dep_graph::DepGraph;
-use rustc_middle::ty::query::{ExternProviders, Providers};
+use rustc_middle::query::{ExternProviders, Providers};
 use rustc_middle::ty::{self, GlobalCtxt, RegisteredTools, TyCtxt};
 use rustc_mir_build as mir_build;
 use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_attr};
 use rustc_passes::{self, hir_stats, layout_test};
 use rustc_plugin_impl as plugin;
-use rustc_query_impl::{OnDiskCache, Queries as TcxQueries};
-use rustc_resolve::{Resolver, ResolverArenas};
-use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType};
-use rustc_session::cstore::{CrateStoreDyn, MetadataLoader, MetadataLoaderDyn};
-use rustc_session::output::{filename_for_input, filename_for_metadata};
+use rustc_resolve::Resolver;
+use rustc_session::code_stats::VTableSizeInfo;
+use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
+use rustc_session::cstore::{MetadataLoader, Untracked};
+use rustc_session::output::filename_for_input;
 use rustc_session::search_paths::PathKind;
 use rustc_session::{Limit, Session};
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::FileName;
+use rustc_target::spec::PanicStrategy;
 use rustc_trait_selection::traits;
-use rustc_typeck as typeck;
-use tempfile::Builder as TempFileBuilder;
-use tracing::{info, warn};
 
 use std::any::Any;
-use std::cell::RefCell;
 use std::ffi::OsString;
 use std::io::{self, BufWriter, Write};
-use std::marker::PhantomPinned;
 use std::path::{Path, PathBuf};
-use std::pin::Pin;
-use std::rc::Rc;
-use std::sync::LazyLock;
+use std::sync::{Arc, LazyLock};
 use std::{env, fs, iter};
 
-pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> {
-    let krate = sess.time("parse_crate", || match input {
+pub fn parse<'a>(sess: &'a Session) -> PResult<'a, ast::Crate> {
+    let krate = sess.time("parse_crate", || match &sess.io.input {
         Input::File(file) => parse_crate_from_file(file, &sess.parse_sess),
         Input::Str { input, name } => {
             parse_crate_from_source_str(name.clone(), input.clone(), &sess.parse_sess)
         }
     })?;
 
-    if sess.opts.debugging_opts.input_stats {
+    if sess.opts.unstable_opts.input_stats {
         eprintln!("Lines of code:             {}", sess.source_map().count_lines());
         eprintln!("Pre-expansion node count:  {}", count_nodes(&krate));
     }
 
-    if let Some(ref s) = sess.opts.debugging_opts.show_span {
+    if let Some(ref s) = sess.opts.unstable_opts.show_span {
         rustc_ast_passes::show_span::run(sess.diagnostic(), s, &krate);
     }
 
-    if sess.opts.debugging_opts.hir_stats {
-        hir_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS");
+    if sess.opts.unstable_opts.hir_stats {
+        hir_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS", "ast-stats-1");
     }
 
     Ok(krate)
@@ -81,122 +72,25 @@ fn count_nodes(krate: &ast::Crate) -> usize {
     counter.count
 }
 
-pub use boxed_resolver::BoxedResolver;
-mod boxed_resolver {
-    use super::*;
-
-    pub struct BoxedResolver(Pin<Box<BoxedResolverInner>>);
-
-    struct BoxedResolverInner {
-        session: Lrc<Session>,
-        resolver_arenas: Option<ResolverArenas<'static>>,
-        resolver: Option<Resolver<'static>>,
-        _pin: PhantomPinned,
-    }
-
-    // Note: Drop order is important to prevent dangling references. Resolver must be dropped first,
-    // then resolver_arenas and session.
-    impl Drop for BoxedResolverInner {
-        fn drop(&mut self) {
-            self.resolver.take();
-            self.resolver_arenas.take();
-        }
-    }
-
-    impl BoxedResolver {
-        pub(super) fn new(
-            session: Lrc<Session>,
-            make_resolver: impl for<'a> FnOnce(&'a Session, &'a ResolverArenas<'a>) -> Resolver<'a>,
-        ) -> BoxedResolver {
-            let mut boxed_resolver = Box::new(BoxedResolverInner {
-                session,
-                resolver_arenas: Some(Resolver::arenas()),
-                resolver: None,
-                _pin: PhantomPinned,
-            });
-            // SAFETY: `make_resolver` takes a resolver arena with an arbitrary lifetime and
-            // returns a resolver with the same lifetime as the arena. We ensure that the arena
-            // outlives the resolver in the drop impl and elsewhere so these transmutes are sound.
-            unsafe {
-                let resolver = make_resolver(
-                    std::mem::transmute::<&Session, &Session>(&boxed_resolver.session),
-                    std::mem::transmute::<&ResolverArenas<'_>, &ResolverArenas<'_>>(
-                        boxed_resolver.resolver_arenas.as_ref().unwrap(),
-                    ),
-                );
-                boxed_resolver.resolver = Some(resolver);
-                BoxedResolver(Pin::new_unchecked(boxed_resolver))
-            }
-        }
-
-        pub fn access<F: for<'a> FnOnce(&mut Resolver<'a>) -> R, R>(&mut self, f: F) -> R {
-            // SAFETY: The resolver doesn't need to be pinned.
-            let mut resolver = unsafe {
-                self.0.as_mut().map_unchecked_mut(|boxed_resolver| &mut boxed_resolver.resolver)
-            };
-            f((&mut *resolver).as_mut().unwrap())
-        }
-
-        pub fn to_resolver_outputs(
-            resolver: Rc<RefCell<BoxedResolver>>,
-        ) -> (Definitions, Box<CrateStoreDyn>, ty::ResolverOutputs, ty::ResolverAstLowering)
-        {
-            match Rc::try_unwrap(resolver) {
-                Ok(resolver) => {
-                    let mut resolver = resolver.into_inner();
-                    // SAFETY: The resolver doesn't need to be pinned.
-                    let mut resolver = unsafe {
-                        resolver
-                            .0
-                            .as_mut()
-                            .map_unchecked_mut(|boxed_resolver| &mut boxed_resolver.resolver)
-                    };
-                    resolver.take().unwrap().into_outputs()
-                }
-                Err(resolver) => resolver.borrow_mut().access(|resolver| resolver.clone_outputs()),
-            }
-        }
-    }
-}
-
-pub fn create_resolver(
-    sess: Lrc<Session>,
-    metadata_loader: Box<MetadataLoaderDyn>,
-    krate: &ast::Crate,
-    crate_name: &str,
-) -> BoxedResolver {
-    tracing::trace!("create_resolver");
-    BoxedResolver::new(sess, move |sess, resolver_arenas| {
-        Resolver::new(sess, krate, crate_name, metadata_loader, resolver_arenas)
-    })
-}
-
 pub fn register_plugins<'a>(
     sess: &'a Session,
     metadata_loader: &'a dyn MetadataLoader,
     register_lints: impl Fn(&Session, &mut LintStore),
-    mut krate: ast::Crate,
-    crate_name: &str,
-) -> Result<(ast::Crate, LintStore)> {
-    krate = sess.time("attributes_injection", || {
-        rustc_builtin_macros::cmdline_attrs::inject(
-            krate,
-            &sess.parse_sess,
-            &sess.opts.debugging_opts.crate_attr,
-        )
-    });
-
-    let (krate, features) = rustc_expand::config::features(sess, krate, CRATE_NODE_ID);
+    pre_configured_attrs: &[ast::Attribute],
+    crate_name: Symbol,
+) -> Result<LintStore> {
     // these need to be set "early" so that expansion sees `quote` if enabled.
+    let features = rustc_expand::config::features(sess, pre_configured_attrs);
     sess.init_features(features);
 
-    let crate_types = util::collect_crate_types(sess, &krate.attrs);
+    let crate_types = util::collect_crate_types(sess, pre_configured_attrs);
     sess.init_crate_types(crate_types);
 
     let stable_crate_id = StableCrateId::new(
         crate_name,
         sess.crate_types().contains(&CrateType::Executable),
         sess.opts.cg.metadata.clone(),
+        sess.cfg_version,
     );
     sess.stable_crate_id.set(stable_crate_id).expect("not yet initialized");
     rustc_incremental::prepare_session_directory(sess, crate_name, stable_crate_id)?;
@@ -213,14 +107,12 @@ pub fn register_plugins<'a>(
         });
     }
 
-    let mut lint_store = rustc_lint::new_lint_store(
-        sess.opts.debugging_opts.no_interleave_lints,
-        sess.unstable_options(),
-    );
+    let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints());
     register_lints(sess, &mut lint_store);
 
-    let registrars =
-        sess.time("plugin_loading", || plugin::load::load_plugins(sess, metadata_loader, &krate));
+    let registrars = sess.time("plugin_loading", || {
+        plugin::load::load_plugins(sess, metadata_loader, pre_configured_attrs)
+    });
     sess.time("plugin_registration", || {
         let mut registry = plugin::Registry { lint_store: &mut lint_store };
         for registrar in registrars {
@@ -228,7 +120,7 @@ pub fn register_plugins<'a>(
         }
     });
 
-    Ok((krate, lint_store))
+    Ok(lint_store)
 }
 
 fn pre_expansion_lint<'a>(
@@ -236,19 +128,21 @@ fn pre_expansion_lint<'a>(
     lint_store: &LintStore,
     registered_tools: &RegisteredTools,
     check_node: impl EarlyCheckNode<'a>,
-    node_name: &str,
+    node_name: Symbol,
 ) {
-    sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", node_name).run(|| {
-        rustc_lint::check_ast_node(
-            sess,
-            true,
-            lint_store,
-            registered_tools,
-            None,
-            rustc_lint::BuiltinCombinedPreExpansionLintPass::new(),
-            check_node,
-        );
-    });
+    sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", node_name.as_str()).run(
+        || {
+            rustc_lint::check_ast_node(
+                sess,
+                true,
+                lint_store,
+                registered_tools,
+                None,
+                rustc_lint::BuiltinCombinedPreExpansionLintPass::new(),
+                check_node,
+            );
+        },
+    );
 }
 
 // Cannot implement directly for `LintStore` due to trait coherence.
@@ -262,7 +156,7 @@ impl LintStoreExpand for LintStoreExpandImpl<'_> {
         node_id: ast::NodeId,
         attrs: &[ast::Attribute],
         items: &[rustc_ast::ptr::P<ast::Item>],
-        name: &str,
+        name: Symbol,
     ) {
         pre_expansion_lint(sess, self.0, registered_tools, (node_id, attrs, items), name);
     }
@@ -272,22 +166,30 @@ impl LintStoreExpand for LintStoreExpandImpl<'_> {
 /// syntax expansion, secondary `cfg` expansion, synthesis of a test
 /// harness if one is to be provided, injection of a dependency on the
 /// standard library and prelude, and name resolution.
-pub fn configure_and_expand(
-    sess: &Session,
-    lint_store: &LintStore,
+#[instrument(level = "trace", skip(krate, resolver))]
+fn configure_and_expand(
     mut krate: ast::Crate,
-    crate_name: &str,
-    resolver: &mut Resolver<'_>,
-) -> Result<ast::Crate> {
-    tracing::trace!("configure_and_expand");
-    pre_expansion_lint(sess, lint_store, resolver.registered_tools(), &krate, crate_name);
+    pre_configured_attrs: &[ast::Attribute],
+    resolver: &mut Resolver<'_, '_>,
+) -> ast::Crate {
+    let tcx = resolver.tcx();
+    let sess = tcx.sess;
+    let lint_store = unerased_lint_store(tcx);
+    let crate_name = tcx.crate_name(LOCAL_CRATE);
+    let lint_check_node = (&krate, pre_configured_attrs);
+    pre_expansion_lint(sess, lint_store, tcx.registered_tools(()), lint_check_node, crate_name);
     rustc_builtin_macros::register_builtin_macros(resolver);
 
-    krate = sess.time("crate_injection", || {
-        rustc_builtin_macros::standard_library_imports::inject(krate, resolver, sess)
+    let num_standard_library_imports = sess.time("crate_injection", || {
+        rustc_builtin_macros::standard_library_imports::inject(
+            &mut krate,
+            pre_configured_attrs,
+            resolver,
+            sess,
+        )
     });
 
-    util::check_attr_crate_type(sess, &krate.attrs, &mut resolver.lint_buffer());
+    util::check_attr_crate_type(sess, pre_configured_attrs, &mut resolver.lint_buffer());
 
     // Expand all macros
     krate = sess.time("macro_expand_crate", || {
@@ -324,46 +226,49 @@ pub fn configure_and_expand(
 
         // Create the config for macro expansion
         let features = sess.features_untracked();
-        let recursion_limit = get_recursion_limit(&krate.attrs, sess);
+        let recursion_limit = get_recursion_limit(pre_configured_attrs, sess);
         let cfg = rustc_expand::expand::ExpansionConfig {
             features: Some(features),
             recursion_limit,
-            trace_mac: sess.opts.debugging_opts.trace_macros,
-            should_test: sess.opts.test,
-            span_debug: sess.opts.debugging_opts.span_debug,
-            proc_macro_backtrace: sess.opts.debugging_opts.proc_macro_backtrace,
+            trace_mac: sess.opts.unstable_opts.trace_macros,
+            should_test: sess.is_test_crate(),
+            span_debug: sess.opts.unstable_opts.span_debug,
+            proc_macro_backtrace: sess.opts.unstable_opts.proc_macro_backtrace,
             ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string())
         };
 
         let lint_store = LintStoreExpandImpl(lint_store);
         let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&lint_store));
-
+        ecx.num_standard_library_imports = num_standard_library_imports;
         // Expand macros now!
         let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));
 
         // The rest is error reporting
 
+        sess.parse_sess.buffered_lints.with_lock(|buffered_lints: &mut Vec<BufferedEarlyLint>| {
+            buffered_lints.append(&mut ecx.buffered_early_lint);
+        });
+
         sess.time("check_unused_macros", || {
             ecx.check_unused_macros();
         });
 
-        let recursion_limit_hit = ecx.reduced_recursion_limit.is_some();
+        // If we hit a recursion limit, exit early to avoid later passes getting overwhelmed
+        // with a large AST
+        if ecx.reduced_recursion_limit.is_some() {
+            sess.abort_if_errors();
+            unreachable!();
+        }
 
         if cfg!(windows) {
             env::set_var("PATH", &old_path);
         }
 
-        if recursion_limit_hit {
-            // If we hit a recursion limit, exit early to avoid later passes getting overwhelmed
-            // with a large AST
-            Err(ErrorGuaranteed::unchecked_claim_error_was_emitted())
-        } else {
-            Ok(krate)
-        }
-    })?;
+        krate
+    });
 
     sess.time("maybe_building_test_harness", || {
-        rustc_builtin_macros::test_harness::inject(sess, resolver, &mut krate)
+        rustc_builtin_macros::test_harness::inject(&mut krate, sess, resolver)
     });
 
     let has_proc_macro_decls = sess.time("AST_validation", || {
@@ -376,54 +281,50 @@ pub fn configure_and_expand(
 
     if crate_types.len() > 1 {
         if is_executable_crate {
-            sess.err("cannot mix `bin` crate type with others");
+            sess.emit_err(errors::MixedBinCrate);
         }
         if is_proc_macro_crate {
-            sess.err("cannot mix `proc-macro` crate type with others");
+            sess.emit_err(errors::MixedProcMacroCrate);
         }
     }
 
-    // For backwards compatibility, we don't try to run proc macro injection
-    // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being
-    // specified. This should only affect users who manually invoke 'rustdoc', as
-    // 'cargo doc' will automatically pass the proper '--crate-type' flags.
-    // However, we do emit a warning, to let such users know that they should
-    // start passing '--crate-type proc-macro'
-    if has_proc_macro_decls && sess.opts.actually_rustdoc && !is_proc_macro_crate {
-        let mut msg = sess.diagnostic().struct_warn(
-            "Trying to document proc macro crate \
-             without passing '--crate-type proc-macro to rustdoc",
-        );
-
-        msg.warn("The generated documentation may be incorrect");
-        msg.emit();
-    } else {
-        krate = sess.time("maybe_create_a_macro_crate", || {
-            let is_test_crate = sess.opts.test;
-            rustc_builtin_macros::proc_macro_harness::inject(
-                sess,
-                resolver,
-                krate,
-                is_proc_macro_crate,
-                has_proc_macro_decls,
-                is_test_crate,
-                sess.diagnostic(),
-            )
-        });
+    if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort {
+        sess.emit_warning(errors::ProcMacroCratePanicAbort);
     }
 
+    sess.time("maybe_create_a_macro_crate", || {
+        let is_test_crate = sess.is_test_crate();
+        rustc_builtin_macros::proc_macro_harness::inject(
+            &mut krate,
+            sess,
+            resolver,
+            is_proc_macro_crate,
+            has_proc_macro_decls,
+            is_test_crate,
+            sess.diagnostic(),
+        )
+    });
+
     // Done with macro expansion!
 
-    if sess.opts.debugging_opts.input_stats {
+    resolver.resolve_crate(&krate);
+
+    krate
+}
+
+fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) {
+    let sess = tcx.sess;
+    let (resolver, krate) = &*tcx.resolver_for_lowering(()).borrow();
+    let mut lint_buffer = resolver.lint_buffer.steal();
+
+    if sess.opts.unstable_opts.input_stats {
         eprintln!("Post-expansion node count: {}", count_nodes(&krate));
     }
 
-    if sess.opts.debugging_opts.hir_stats {
-        hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS");
+    if sess.opts.unstable_opts.hir_stats {
+        hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS", "ast-stats-2");
     }
 
-    resolver.resolve_crate(&krate);
-
     // Needs to go *after* expansion to be able to check the results of macro expansion.
     sess.time("complete_gated_feature_checking", || {
         rustc_ast_passes::feature_gate::check_crate(&krate, sess);
@@ -433,7 +334,7 @@ pub fn configure_and_expand(
     sess.parse_sess.buffered_lints.with_lock(|buffered_lints| {
         info!("{} parse sess buffered_lints", buffered_lints.len());
         for early_lint in buffered_lints.drain(..) {
-            resolver.lint_buffer().add_early_lint(early_lint);
+            lint_buffer.add_early_lint(early_lint);
         }
     });
 
@@ -445,72 +346,23 @@ pub fn configure_and_expand(
             spans.sort();
             if ident == sym::ferris {
                 let first_span = spans[0];
-                sess.diagnostic()
-                    .struct_span_err(
-                        MultiSpan::from(spans),
-                        "Ferris cannot be used as an identifier",
-                    )
-                    .span_suggestion(
-                        first_span,
-                        "try using their name instead",
-                        "ferris",
-                        Applicability::MaybeIncorrect,
-                    )
-                    .emit();
+                sess.emit_err(errors::FerrisIdentifier { spans, first_span });
             } else {
-                sess.diagnostic().span_err(
-                    MultiSpan::from(spans),
-                    &format!("identifiers cannot contain emoji: `{}`", ident),
-                );
+                sess.emit_err(errors::EmojiIdentifier { spans, ident });
             }
         }
     });
 
-    sess.time("early_lint_checks", || {
-        let lint_buffer = Some(std::mem::take(resolver.lint_buffer()));
-        rustc_lint::check_ast_node(
-            sess,
-            false,
-            lint_store,
-            resolver.registered_tools(),
-            lint_buffer,
-            rustc_lint::BuiltinCombinedEarlyLintPass::new(),
-            &krate,
-        )
-    });
-
-    Ok(krate)
-}
-
-fn lower_to_hir<'tcx>(
-    sess: &Session,
-    definitions: &mut Definitions,
-    cstore: &CrateStoreDyn,
-    resolutions: &ty::ResolverOutputs,
-    resolver: ty::ResolverAstLowering,
-    krate: Rc<ast::Crate>,
-    arena: &'tcx rustc_ast_lowering::Arena<'tcx>,
-) -> &'tcx Crate<'tcx> {
-    // Lower AST to HIR.
-    let hir_crate = rustc_ast_lowering::lower_crate(
+    let lint_store = unerased_lint_store(tcx);
+    rustc_lint::check_ast_node(
         sess,
-        &krate,
-        definitions,
-        cstore,
-        resolutions,
-        resolver,
-        arena,
-    );
-
-    // Drop AST to free memory
-    sess.time("drop_ast", || std::mem::drop(krate));
-
-    // Discard hygiene data, which isn't required after lowering to HIR.
-    if !sess.opts.debugging_opts.keep_hygiene_data {
-        rustc_span::hygiene::clear_syntax_context_map();
-    }
-
-    hir_crate
+        false,
+        lint_store,
+        tcx.registered_tools(()),
+        Some(lint_buffer),
+        rustc_lint::BuiltinCombinedEarlyLintPass::new(),
+        (&**krate, &*krate.attrs),
+    )
 }
 
 // Returns all the paths that correspond to generated files.
@@ -518,23 +370,27 @@ fn generated_output_paths(
     sess: &Session,
     outputs: &OutputFilenames,
     exact_name: bool,
-    crate_name: &str,
+    crate_name: Symbol,
 ) -> Vec<PathBuf> {
     let mut out_filenames = Vec::new();
     for output_type in sess.opts.output_types.keys() {
-        let file = outputs.path(*output_type);
+        let out_filename = outputs.path(*output_type);
+        let file = out_filename.as_path().to_path_buf();
         match *output_type {
             // If the filename has been overridden using `-o`, it will not be modified
             // by appending `.rlib`, `.exe`, etc., so we can skip this transformation.
             OutputType::Exe if !exact_name => {
                 for crate_type in sess.crate_types().iter() {
                     let p = filename_for_input(sess, *crate_type, crate_name, outputs);
-                    out_filenames.push(p);
+                    out_filenames.push(p.as_path().to_path_buf());
                 }
             }
-            OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => {
+            OutputType::DepInfo if sess.opts.unstable_opts.dep_info_omit_d_target => {
                 // Don't add the dep-info output when omitting it from dep-info targets
             }
+            OutputType::DepInfo if out_filename.is_stdout() => {
+                // Don't add the dep-info output when it goes to stdout
+            }
             _ => {
                 out_filenames.push(file);
             }
@@ -558,12 +414,12 @@ where
 }
 
 fn output_contains_path(output_paths: &[PathBuf], input_path: &Path) -> bool {
-    let input_path = input_path.canonicalize().ok();
+    let input_path = try_canonicalize(input_path).ok();
     if input_path.is_none() {
         return false;
     }
     let check = |output_path: &PathBuf| {
-        if output_path.canonicalize().ok() == input_path { Some(()) } else { None }
+        if try_canonicalize(output_path).ok() == input_path { Some(()) } else { None }
     };
     check_output(output_paths, check).is_some()
 }
@@ -595,19 +451,16 @@ fn escape_dep_env(symbol: Symbol) -> String {
     escaped
 }
 
-fn write_out_deps(
-    sess: &Session,
-    boxed_resolver: &RefCell<BoxedResolver>,
-    outputs: &OutputFilenames,
-    out_filenames: &[PathBuf],
-) {
+fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[PathBuf]) {
     // Write out dependency rules to the dep-info file if requested
+    let sess = tcx.sess;
     if !sess.opts.output_types.contains_key(&OutputType::DepInfo) {
         return;
     }
-    let deps_filename = outputs.path(OutputType::DepInfo);
+    let deps_output = outputs.path(OutputType::DepInfo);
+    let deps_filename = deps_output.as_path();
 
-    let result = (|| -> io::Result<()> {
+    let result: io::Result<()> = try {
         // Build a list of files used to compile the output and
         // write Makefile-compatible dependency rules
         let mut files: Vec<String> = sess
@@ -622,15 +475,31 @@ fn write_out_deps(
         // Account for explicitly marked-to-track files
         // (e.g. accessed in proc macros).
         let file_depinfo = sess.parse_sess.file_depinfo.borrow();
-        let extra_tracked_files = file_depinfo.iter().map(|path_sym| {
-            let path = PathBuf::from(path_sym.as_str());
+
+        let normalize_path = |path: PathBuf| {
             let file = FileName::from(path);
             escape_dep_filename(&file.prefer_local().to_string())
-        });
+        };
+
+        let extra_tracked_files =
+            file_depinfo.iter().map(|path_sym| normalize_path(PathBuf::from(path_sym.as_str())));
         files.extend(extra_tracked_files);
 
+        // We also need to track used PGO profile files
+        if let Some(ref profile_instr) = sess.opts.cg.profile_use {
+            files.push(normalize_path(profile_instr.as_path().to_path_buf()));
+        }
+        if let Some(ref profile_sample) = sess.opts.unstable_opts.profile_sample_use {
+            files.push(normalize_path(profile_sample.as_path().to_path_buf()));
+        }
+
+        // Debugger visualizer files
+        for debugger_visualizer in tcx.debugger_visualizers(LOCAL_CRATE) {
+            files.push(normalize_path(debugger_visualizer.path.clone().unwrap()));
+        }
+
         if sess.binary_dep_depinfo() {
-            if let Some(ref backend) = sess.opts.debugging_opts.codegen_backend {
+            if let Some(ref backend) = sess.opts.unstable_opts.codegen_backend {
                 if backend.contains('.') {
                     // If the backend name contain a `.`, it is the path to an external dynamic
                     // library. If not, it is not a path.
@@ -638,54 +507,64 @@ fn write_out_deps(
                 }
             }
 
-            boxed_resolver.borrow_mut().access(|resolver| {
-                for cnum in resolver.cstore().crates_untracked() {
-                    let source = resolver.cstore().crate_source_untracked(cnum);
-                    if let Some((path, _)) = &source.dylib {
-                        files.push(escape_dep_filename(&path.display().to_string()));
-                    }
-                    if let Some((path, _)) = &source.rlib {
-                        files.push(escape_dep_filename(&path.display().to_string()));
-                    }
-                    if let Some((path, _)) = &source.rmeta {
-                        files.push(escape_dep_filename(&path.display().to_string()));
-                    }
+            for &cnum in tcx.crates(()) {
+                let source = tcx.used_crate_source(cnum);
+                if let Some((path, _)) = &source.dylib {
+                    files.push(escape_dep_filename(&path.display().to_string()));
                 }
-            });
+                if let Some((path, _)) = &source.rlib {
+                    files.push(escape_dep_filename(&path.display().to_string()));
+                }
+                if let Some((path, _)) = &source.rmeta {
+                    files.push(escape_dep_filename(&path.display().to_string()));
+                }
+            }
         }
 
-        let mut file = BufWriter::new(fs::File::create(&deps_filename)?);
-        for path in out_filenames {
-            writeln!(file, "{}: {}\n", path.display(), files.join(" "))?;
-        }
+        let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> {
+            for path in out_filenames {
+                writeln!(file, "{}: {}\n", path.display(), files.join(" "))?;
+            }
 
-        // Emit a fake target for each input file to the compilation. This
-        // prevents `make` from spitting out an error if a file is later
-        // deleted. For more info see #28735
-        for path in files {
-            writeln!(file, "{}:", path)?;
-        }
+            // Emit a fake target for each input file to the compilation. This
+            // prevents `make` from spitting out an error if a file is later
+            // deleted. For more info see #28735
+            for path in files {
+                writeln!(file, "{path}:")?;
+            }
 
-        // Emit special comments with information about accessed environment variables.
-        let env_depinfo = sess.parse_sess.env_depinfo.borrow();
-        if !env_depinfo.is_empty() {
-            let mut envs: Vec<_> = env_depinfo
-                .iter()
-                .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env)))
-                .collect();
-            envs.sort_unstable();
-            writeln!(file)?;
-            for (k, v) in envs {
-                write!(file, "# env-dep:{}", k)?;
-                if let Some(v) = v {
-                    write!(file, "={}", v)?;
-                }
+            // Emit special comments with information about accessed environment variables.
+            let env_depinfo = sess.parse_sess.env_depinfo.borrow();
+            if !env_depinfo.is_empty() {
+                let mut envs: Vec<_> = env_depinfo
+                    .iter()
+                    .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env)))
+                    .collect();
+                envs.sort_unstable();
                 writeln!(file)?;
+                for (k, v) in envs {
+                    write!(file, "# env-dep:{k}")?;
+                    if let Some(v) = v {
+                        write!(file, "={v}")?;
+                    }
+                    writeln!(file)?;
+                }
             }
-        }
 
-        Ok(())
-    })();
+            Ok(())
+        };
+
+        match deps_output {
+            OutFileName::Stdout => {
+                let mut file = BufWriter::new(io::stdout());
+                write_deps_to_file(&mut file)?;
+            }
+            OutFileName::Real(ref path) => {
+                let mut file = BufWriter::new(fs::File::create(path)?);
+                write_deps_to_file(&mut file)?;
+            }
+        }
+    };
 
     match result {
         Ok(_) => {
@@ -695,88 +574,91 @@ fn write_out_deps(
                     .emit_artifact_notification(&deps_filename, "dep-info");
             }
         }
-        Err(e) => sess.fatal(&format!(
-            "error writing dependencies to `{}`: {}",
-            deps_filename.display(),
-            e
-        )),
+        Err(error) => {
+            sess.emit_fatal(errors::ErrorWritingDependencies { path: &deps_filename, error });
+        }
     }
 }
 
-pub fn prepare_outputs(
-    sess: &Session,
-    compiler: &Compiler,
-    krate: &ast::Crate,
-    boxed_resolver: &RefCell<BoxedResolver>,
-    crate_name: &str,
-) -> Result<OutputFilenames> {
+fn resolver_for_lowering<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    (): (),
+) -> &'tcx Steal<(ty::ResolverAstLowering, Lrc<ast::Crate>)> {
+    let arenas = Resolver::arenas();
+    let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`.
+    let (krate, pre_configured_attrs) = tcx.crate_for_resolver(()).steal();
+    let mut resolver = Resolver::new(tcx, &pre_configured_attrs, krate.spans.inner_span, &arenas);
+    let krate = configure_and_expand(krate, &pre_configured_attrs, &mut resolver);
+
+    // Make sure we don't mutate the cstore from here on.
+    tcx.untracked().cstore.leak();
+
+    let ty::ResolverOutputs {
+        global_ctxt: untracked_resolutions,
+        ast_lowering: untracked_resolver_for_lowering,
+    } = resolver.into_outputs();
+
+    let feed = tcx.feed_unit_query();
+    feed.resolutions(tcx.arena.alloc(untracked_resolutions));
+    tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, Lrc::new(krate))))
+}
+
+fn output_filenames(tcx: TyCtxt<'_>, (): ()) -> Arc<OutputFilenames> {
+    let sess = tcx.sess;
     let _timer = sess.timer("prepare_outputs");
+    let (_, krate) = &*tcx.resolver_for_lowering(()).borrow();
+    let crate_name = tcx.crate_name(LOCAL_CRATE);
 
     // FIXME: rustdoc passes &[] instead of &krate.attrs here
-    let outputs = util::build_output_filenames(
-        &compiler.input,
-        &compiler.output_dir,
-        &compiler.output_file,
-        &compiler.temps_dir,
-        &krate.attrs,
-        sess,
-    );
+    let outputs = util::build_output_filenames(&krate.attrs, sess);
 
     let output_paths =
-        generated_output_paths(sess, &outputs, compiler.output_file.is_some(), crate_name);
+        generated_output_paths(sess, &outputs, sess.io.output_file.is_some(), crate_name);
 
     // Ensure the source file isn't accidentally overwritten during compilation.
-    if let Some(ref input_path) = compiler.input_path {
+    if let Some(ref input_path) = sess.io.input.opt_path() {
         if sess.opts.will_create_output_file() {
             if output_contains_path(&output_paths, input_path) {
-                let reported = sess.err(&format!(
-                    "the input file \"{}\" would be overwritten by the generated \
-                        executable",
-                    input_path.display()
-                ));
-                return Err(reported);
+                sess.emit_fatal(errors::InputFileWouldBeOverWritten { path: input_path });
             }
-            if let Some(dir_path) = output_conflicts_with_dir(&output_paths) {
-                let reported = sess.err(&format!(
-                    "the generated executable for the input file \"{}\" conflicts with the \
-                        existing directory \"{}\"",
-                    input_path.display(),
-                    dir_path.display()
-                ));
-                return Err(reported);
+            if let Some(ref dir_path) = output_conflicts_with_dir(&output_paths) {
+                sess.emit_fatal(errors::GeneratedFileConflictsWithDirectory {
+                    input_path,
+                    dir_path,
+                });
             }
         }
     }
 
-    if let Some(ref dir) = compiler.temps_dir {
+    if let Some(ref dir) = sess.io.temps_dir {
         if fs::create_dir_all(dir).is_err() {
-            let reported =
-                sess.err("failed to find or create the directory specified by `--temps-dir`");
-            return Err(reported);
+            sess.emit_fatal(errors::TempsDirError);
         }
     }
 
-    write_out_deps(sess, boxed_resolver, &outputs, &output_paths);
+    write_out_deps(tcx, &outputs, &output_paths);
 
     let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo)
         && sess.opts.output_types.len() == 1;
 
     if !only_dep_info {
-        if let Some(ref dir) = compiler.output_dir {
+        if let Some(ref dir) = sess.io.output_dir {
             if fs::create_dir_all(dir).is_err() {
-                let reported =
-                    sess.err("failed to find or create the directory specified by `--out-dir`");
-                return Err(reported);
+                sess.emit_fatal(errors::OutDirError);
             }
         }
     }
 
-    Ok(outputs)
+    outputs.into()
 }
 
 pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
     let providers = &mut Providers::default();
     providers.analysis = analysis;
+    providers.hir_crate = rustc_ast_lowering::lower_to_hir;
+    providers.output_filenames = output_filenames;
+    providers.resolver_for_lowering = resolver_for_lowering;
+    providers.early_lint_checks = early_lint_checks;
     proc_macro_decls::provide(providers);
     rustc_const_eval::provide(providers);
     rustc_middle::hir::provide(providers);
@@ -785,11 +667,12 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
     rustc_mir_transform::provide(providers);
     rustc_monomorphize::provide(providers);
     rustc_privacy::provide(providers);
-    typeck::provide(providers);
+    rustc_resolve::provide(providers);
+    rustc_hir_analysis::provide(providers);
+    rustc_hir_typeck::provide(providers);
     ty::provide(providers);
     traits::provide(providers);
     rustc_passes::provide(providers);
-    rustc_resolve::provide(providers);
     rustc_traits::provide(providers);
     rustc_ty_utils::provide(providers);
     rustc_metadata::provide(providers);
@@ -806,54 +689,21 @@ pub static DEFAULT_EXTERN_QUERY_PROVIDERS: LazyLock<ExternProviders> = LazyLock:
     extern_providers
 });
 
-pub struct QueryContext<'tcx> {
-    gcx: &'tcx GlobalCtxt<'tcx>,
-}
-
-impl<'tcx> QueryContext<'tcx> {
-    pub fn enter<F, R>(&mut self, f: F) -> R
-    where
-        F: FnOnce(TyCtxt<'tcx>) -> R,
-    {
-        let icx = ty::tls::ImplicitCtxt::new(self.gcx);
-        ty::tls::enter_context(&icx, |_| f(icx.tcx))
-    }
-}
-
 pub fn create_global_ctxt<'tcx>(
     compiler: &'tcx Compiler,
     lint_store: Lrc<LintStore>,
-    krate: Rc<ast::Crate>,
     dep_graph: DepGraph,
-    resolver: Rc<RefCell<BoxedResolver>>,
-    outputs: OutputFilenames,
-    crate_name: &str,
-    queries: &'tcx OnceCell<TcxQueries<'tcx>>,
-    global_ctxt: &'tcx OnceCell<GlobalCtxt<'tcx>>,
+    untracked: Untracked,
+    gcx_cell: &'tcx OnceCell<GlobalCtxt<'tcx>>,
     arena: &'tcx WorkerLocal<Arena<'tcx>>,
-    hir_arena: &'tcx WorkerLocal<rustc_ast_lowering::Arena<'tcx>>,
-) -> QueryContext<'tcx> {
+    hir_arena: &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>,
+) -> &'tcx GlobalCtxt<'tcx> {
     // We're constructing the HIR here; we don't care what we will
     // read, since we haven't even constructed the *input* to
     // incr. comp. yet.
     dep_graph.assert_ignored();
 
-    let (mut definitions, cstore, resolver_outputs, resolver_for_lowering) =
-        BoxedResolver::to_resolver_outputs(resolver);
-
     let sess = &compiler.session();
-
-    // Lower AST to HIR.
-    let krate = lower_to_hir(
-        sess,
-        &mut definitions,
-        &*cstore,
-        &resolver_outputs,
-        resolver_for_lowering,
-        krate,
-        hir_arena,
-    );
-
     let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess);
 
     let codegen_backend = compiler.codegen_backend();
@@ -867,35 +717,31 @@ pub fn create_global_ctxt<'tcx>(
         callback(sess, &mut local_providers, &mut extern_providers);
     }
 
-    let queries = queries.get_or_init(|| {
-        TcxQueries::new(local_providers, extern_providers, query_result_on_disk_cache)
-    });
+    let incremental = dep_graph.is_fully_enabled();
 
-    let gcx = sess.time("setup_global_ctxt", || {
-        global_ctxt.get_or_init(move || {
+    sess.time("setup_global_ctxt", || {
+        gcx_cell.get_or_init(move || {
             TyCtxt::create_global_ctxt(
                 sess,
                 lint_store,
                 arena,
-                definitions,
-                cstore,
-                resolver_outputs,
-                krate,
+                hir_arena,
+                untracked,
                 dep_graph,
-                queries.on_disk_cache.as_ref().map(OnDiskCache::as_dyn),
-                queries.as_dyn(),
                 rustc_query_impl::query_callbacks(arena),
-                crate_name,
-                outputs,
+                rustc_query_impl::query_system(
+                    local_providers,
+                    extern_providers,
+                    query_result_on_disk_cache,
+                    incremental,
+                ),
             )
         })
-    });
-
-    QueryContext { gcx }
+    })
 }
 
-/// Runs the resolution, type-checking, region checking and other
-/// miscellaneous analysis passes on the crate.
+/// Runs the type-checking, region checking and other miscellaneous analysis
+/// passes on the crate.
 fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
     rustc_passes::hir_id_validator::check_crate(tcx);
 
@@ -939,28 +785,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
     });
 
     // passes are timed inside typeck
-    typeck::check_crate(tcx)?;
-
-    sess.time("misc_checking_2", || {
-        parallel!(
-            {
-                sess.time("match_checking", || {
-                    tcx.hir().par_body_owners(|def_id| tcx.ensure().check_match(def_id.to_def_id()))
-                });
-            },
-            {
-                sess.time("liveness_and_intrinsic_checking", || {
-                    tcx.hir().par_for_each_module(|module| {
-                        // this must run before MIR dump, because
-                        // "not all control paths return a value" is reported here.
-                        //
-                        // maybe move the check to a MIR pass?
-                        tcx.ensure().check_mod_liveness(module);
-                    });
-                });
-            }
-        );
-    });
+    rustc_hir_analysis::check_crate(tcx)?;
 
     sess.time("MIR_borrow_checking", || {
         tcx.hir().par_body_owners(|def_id| tcx.ensure().mir_borrowck(def_id));
@@ -969,17 +794,32 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
     sess.time("MIR_effect_checking", || {
         for def_id in tcx.hir().body_owners() {
             tcx.ensure().thir_check_unsafety(def_id);
-            if !tcx.sess.opts.debugging_opts.thir_unsafeck {
+            if !tcx.sess.opts.unstable_opts.thir_unsafeck {
                 rustc_mir_transform::check_unsafety::check_unsafety(tcx, def_id);
             }
+            tcx.ensure().has_ffi_unwind_calls(def_id);
 
-            if tcx.hir().body_const_context(def_id).is_some() {
-                tcx.ensure()
-                    .mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(def_id));
+            // If we need to codegen, ensure that we emit all errors from
+            // `mir_drops_elaborated_and_const_checked` now, to avoid discovering
+            // them later during codegen.
+            if tcx.sess.opts.output_types.should_codegen()
+                || tcx.hir().body_const_context(def_id).is_some()
+            {
+                tcx.ensure().mir_drops_elaborated_and_const_checked(def_id);
+                tcx.ensure().unused_generic_params(ty::InstanceDef::Item(def_id.to_def_id()));
             }
         }
     });
 
+    if tcx.sess.opts.unstable_opts.drop_tracking_mir {
+        tcx.hir().par_body_owners(|def_id| {
+            if let rustc_hir::def::DefKind::Generator = tcx.def_kind(def_id) {
+                tcx.ensure().mir_generator_witnesses(def_id);
+                tcx.ensure().check_generator_obligations(def_id);
+            }
+        });
+    }
+
     sess.time("layout_testing", || layout_test::test_layout(tcx));
 
     // Avoid overwhelming user with errors if borrow checking failed.
@@ -994,7 +834,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
     sess.time("misc_checking_3", || {
         parallel!(
             {
-                tcx.ensure().privacy_access_levels(());
+                tcx.ensure().effective_visibilities(());
 
                 parallel!(
                     {
@@ -1027,70 +867,93 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
         sess.time("check_lint_expectations", || tcx.check_expectations(None));
     });
 
-    Ok(())
-}
+    if sess.opts.unstable_opts.print_vtable_sizes {
+        let traits = tcx.traits(LOCAL_CRATE);
 
-fn encode_and_write_metadata(
-    tcx: TyCtxt<'_>,
-    outputs: &OutputFilenames,
-) -> (EncodedMetadata, bool) {
-    #[derive(PartialEq, Eq, PartialOrd, Ord)]
-    enum MetadataKind {
-        None,
-        Uncompressed,
-        Compressed,
-    }
+        for &tr in traits {
+            if !tcx.check_is_object_safe(tr) {
+                continue;
+            }
 
-    let metadata_kind = tcx
-        .sess
-        .crate_types()
-        .iter()
-        .map(|ty| match *ty {
-            CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None,
+            let name = ty::print::with_no_trimmed_paths!(tcx.def_path_str(tr));
+
+            let mut first_dsa = true;
+
+            // Number of vtable entries, if we didn't have upcasting
+            let mut entries_ignoring_upcasting = 0;
+            // Number of vtable entries needed solely for upcasting
+            let mut entries_for_upcasting = 0;
+
+            let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, tr));
+
+            // A slightly edited version of the code in `rustc_trait_selection::traits::vtable::vtable_entries`,
+            // that works without self type and just counts number of entries.
+            //
+            // Note that this is technically wrong, for traits which have associated types in supertraits:
+            //
+            //   trait A: AsRef<Self::T> + AsRef<()> { type T; }
+            //
+            // Without self type we can't normalize `Self::T`, so we can't know if `AsRef<Self::T>` and
+            // `AsRef<()>` are the same trait, thus we assume that those are different, and potentially
+            // over-estimate how many vtable entries there are.
+            //
+            // Similarly this is wrong for traits that have methods with possibly-impossible bounds.
+            // For example:
+            //
+            //   trait B<T> { fn f(&self) where T: Copy; }
+            //
+            // Here `dyn B<u8>` will have 4 entries, while `dyn B<String>` will only have 3.
+            // However, since we don't know `T`, we can't know if `T: Copy` holds or not,
+            // thus we lean on the bigger side and say it has 4 entries.
+            traits::vtable::prepare_vtable_segments(tcx, trait_ref, |segment| {
+                match segment {
+                    traits::vtable::VtblSegment::MetadataDSA => {
+                        // If this is the first dsa, it would be included either way,
+                        // otherwise it's needed for upcasting
+                        if std::mem::take(&mut first_dsa) {
+                            entries_ignoring_upcasting += 3;
+                        } else {
+                            entries_for_upcasting += 3;
+                        }
+                    }
 
-            CrateType::Rlib => MetadataKind::Uncompressed,
+                    traits::vtable::VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+                        // Lookup the shape of vtable for the trait.
+                        let own_existential_entries =
+                            tcx.own_existential_vtable_entries(trait_ref.def_id());
 
-            CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
-        })
-        .max()
-        .unwrap_or(MetadataKind::None);
+                        // The original code here ignores the method if its predicates are impossible.
+                        // We can't really do that as, for example, all not trivial bounds on generic
+                        // parameters are impossible (since we don't know the parameters...),
+                        // see the comment above.
+                        entries_ignoring_upcasting += own_existential_entries.len();
 
-    let metadata = match metadata_kind {
-        MetadataKind::None => EncodedMetadata::new(),
-        MetadataKind::Uncompressed | MetadataKind::Compressed => encode_metadata(tcx),
-    };
+                        if emit_vptr {
+                            entries_for_upcasting += 1;
+                        }
+                    }
+                }
 
-    let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");
-
-    let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
-    if need_metadata_file {
-        let crate_name = tcx.crate_name(LOCAL_CRATE);
-        let out_filename = filename_for_metadata(tcx.sess, crate_name.as_str(), outputs);
-        // To avoid races with another rustc process scanning the output directory,
-        // we need to write the file somewhere else and atomically move it to its
-        // final destination, with an `fs::rename` call. In order for the rename to
-        // always succeed, the temporary file needs to be on the same filesystem,
-        // which is why we create it inside the output directory specifically.
-        let metadata_tmpdir = TempFileBuilder::new()
-            .prefix("rmeta")
-            .tempdir_in(out_filename.parent().unwrap())
-            .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err)));
-        let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
-        let metadata_filename = emit_metadata(tcx.sess, metadata.raw_data(), &metadata_tmpdir);
-        if let Err(e) = util::non_durable_rename(&metadata_filename, &out_filename) {
-            tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
-        }
-        if tcx.sess.opts.json_artifact_notifications {
-            tcx.sess
-                .parse_sess
-                .span_diagnostic
-                .emit_artifact_notification(&out_filename, "metadata");
+                std::ops::ControlFlow::Continue::<std::convert::Infallible>(())
+            });
+
+            sess.code_stats.record_vtable_size(
+                tr,
+                &name,
+                VTableSizeInfo {
+                    trait_name: name.clone(),
+                    entries: entries_ignoring_upcasting + entries_for_upcasting,
+                    entries_ignoring_upcasting,
+                    entries_for_upcasting,
+                    upcasting_cost_percent: entries_for_upcasting as f64
+                        / entries_ignoring_upcasting as f64
+                        * 100.,
+                },
+            )
         }
     }
 
-    let need_metadata_module = metadata_kind == MetadataKind::Compressed;
-
-    (metadata, need_metadata_module)
+    Ok(())
 }
 
 /// Runs the codegen backend, after which the AST and analysis can
@@ -1098,11 +961,10 @@ fn encode_and_write_metadata(
 pub fn start_codegen<'tcx>(
     codegen_backend: &dyn CodegenBackend,
     tcx: TyCtxt<'tcx>,
-    outputs: &OutputFilenames,
 ) -> Box<dyn Any> {
     info!("Pre-codegen\n{:?}", tcx.debug_stats());
 
-    let (metadata, need_metadata_module) = encode_and_write_metadata(tcx, outputs);
+    let (metadata, need_metadata_module) = rustc_metadata::fs::encode_and_write_metadata(tcx);
 
     let codegen = tcx.sess.time("codegen_crate", move || {
         codegen_backend.codegen_crate(tcx, metadata, need_metadata_module)
@@ -1118,8 +980,8 @@ pub fn start_codegen<'tcx>(
     info!("Post-codegen\n{:?}", tcx.debug_stats());
 
     if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) {
-        if let Err(e) = rustc_mir_transform::dump_mir::emit_mir(tcx, outputs) {
-            tcx.sess.err(&format!("could not emit MIR: {}", e));
+        if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) {
+            tcx.sess.emit_err(errors::CantEmitMIR { error });
             tcx.sess.abort_if_errors();
         }
     }
diff --git a/compiler/rustc_interface/src/proc_macro_decls.rs b/compiler/rustc_interface/src/proc_macro_decls.rs
index 5371c513d29..2c8014d8b3a 100644
--- a/compiler/rustc_interface/src/proc_macro_decls.rs
+++ b/compiler/rustc_interface/src/proc_macro_decls.rs
@@ -1,25 +1,20 @@
-use rustc_hir as hir;
+use rustc_ast::attr;
 use rustc_hir::def_id::LocalDefId;
-use rustc_middle::ty::query::Providers;
+use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::sym;
 
 fn proc_macro_decls_static(tcx: TyCtxt<'_>, (): ()) -> Option<LocalDefId> {
-    let mut finder = Finder { tcx, decls: None };
+    let mut decls = None;
 
     for id in tcx.hir().items() {
-        let attrs = finder.tcx.hir().attrs(id.hir_id());
-        if finder.tcx.sess.contains_name(attrs, sym::rustc_proc_macro_decls) {
-            finder.decls = Some(id.def_id);
+        let attrs = tcx.hir().attrs(id.hir_id());
+        if attr::contains_name(attrs, sym::rustc_proc_macro_decls) {
+            decls = Some(id.owner_id.def_id);
         }
     }
 
-    finder.decls
-}
-
-struct Finder<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    decls: Option<hir::def_id::LocalDefId>,
+    decls
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 136f0443fa0..8c4cdc6696a 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -1,62 +1,74 @@
+use crate::errors::{FailedWritingFile, RustcErrorFatal, RustcErrorUnexpectedAnnotation};
 use crate::interface::{Compiler, Result};
-use crate::passes::{self, BoxedResolver, QueryContext};
+use crate::passes;
 
 use rustc_ast as ast;
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_codegen_ssa::CodegenResults;
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::steal::Steal;
 use rustc_data_structures::svh::Svh;
-use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
-use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_data_structures::sync::{AppendOnlyIndexVec, Lrc, OnceCell, RwLock, WorkerLocal};
+use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
+use rustc_hir::definitions::Definitions;
 use rustc_incremental::DepGraphFuture;
 use rustc_lint::LintStore;
+use rustc_metadata::creader::CStore;
 use rustc_middle::arena::Arena;
 use rustc_middle::dep_graph::DepGraph;
 use rustc_middle::ty::{GlobalCtxt, TyCtxt};
-use rustc_query_impl::Queries as TcxQueries;
 use rustc_session::config::{self, OutputFilenames, OutputType};
+use rustc_session::cstore::Untracked;
 use rustc_session::{output::find_crate_name, Session};
 use rustc_span::symbol::sym;
+use rustc_span::Symbol;
 use std::any::Any;
-use std::cell::{Ref, RefCell, RefMut};
-use std::rc::Rc;
+use std::cell::{RefCell, RefMut};
+use std::sync::Arc;
 
 /// Represent the result of a query.
 ///
-/// This result can be stolen with the [`take`] method and generated with the [`compute`] method.
+/// This result can be stolen once with the [`steal`] method and generated with the [`compute`] method.
 ///
-/// [`take`]: Self::take
+/// [`steal`]: Steal::steal
 /// [`compute`]: Self::compute
 pub struct Query<T> {
-    result: RefCell<Option<Result<T>>>,
+    /// `None` means no value has been computed yet.
+    result: RefCell<Option<Result<Steal<T>>>>,
 }
 
 impl<T> Query<T> {
-    fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<&Query<T>> {
-        let mut result = self.result.borrow_mut();
-        if result.is_none() {
-            *result = Some(f());
-        }
-        result.as_ref().unwrap().as_ref().map(|_| self).map_err(|err| *err)
+    fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<QueryResult<'_, T>> {
+        RefMut::filter_map(
+            self.result.borrow_mut(),
+            |r: &mut Option<Result<Steal<T>>>| -> Option<&mut Steal<T>> {
+                r.get_or_insert_with(|| f().map(Steal::new)).as_mut().ok()
+            },
+        )
+        .map_err(|r| *r.as_ref().unwrap().as_ref().map(|_| ()).unwrap_err())
+        .map(QueryResult)
     }
+}
+
+pub struct QueryResult<'a, T>(RefMut<'a, Steal<T>>);
 
-    /// Takes ownership of the query result. Further attempts to take or peek the query
-    /// result will panic unless it is generated by calling the `compute` method.
-    pub fn take(&self) -> T {
-        self.result.borrow_mut().take().expect("missing query result").unwrap()
+impl<'a, T> std::ops::Deref for QueryResult<'a, T> {
+    type Target = RefMut<'a, Steal<T>>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
     }
+}
 
-    /// Borrows the query result using the RefCell. Panics if the result is stolen.
-    pub fn peek(&self) -> Ref<'_, T> {
-        Ref::map(self.result.borrow(), |r| {
-            r.as_ref().unwrap().as_ref().expect("missing query result")
-        })
+impl<'a, T> std::ops::DerefMut for QueryResult<'a, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
     }
+}
 
-    /// Mutably borrows the query result using the RefCell. Panics if the result is stolen.
-    pub fn peek_mut(&self) -> RefMut<'_, T> {
-        RefMut::map(self.result.borrow_mut(), |r| {
-            r.as_mut().unwrap().as_mut().expect("missing query result")
-        })
+impl<'a, 'tcx> QueryResult<'a, &'tcx GlobalCtxt<'tcx>> {
+    pub fn enter<T>(&mut self, f: impl FnOnce(TyCtxt<'tcx>) -> T) -> T {
+        (*self.0).get_mut().enter(f)
     }
 }
 
@@ -68,76 +80,88 @@ impl<T> Default for Query<T> {
 
 pub struct Queries<'tcx> {
     compiler: &'tcx Compiler,
-    gcx: OnceCell<GlobalCtxt<'tcx>>,
-    queries: OnceCell<TcxQueries<'tcx>>,
+    gcx_cell: OnceCell<GlobalCtxt<'tcx>>,
 
     arena: WorkerLocal<Arena<'tcx>>,
-    hir_arena: WorkerLocal<rustc_ast_lowering::Arena<'tcx>>,
+    hir_arena: WorkerLocal<rustc_hir::Arena<'tcx>>,
 
     dep_graph_future: Query<Option<DepGraphFuture>>,
     parse: Query<ast::Crate>,
-    crate_name: Query<String>,
-    register_plugins: Query<(ast::Crate, Lrc<LintStore>)>,
-    expansion: Query<(Rc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>,
+    pre_configure: Query<(ast::Crate, ast::AttrVec)>,
+    crate_name: Query<Symbol>,
+    register_plugins: Query<(ast::Crate, ast::AttrVec, Lrc<LintStore>)>,
     dep_graph: Query<DepGraph>,
-    prepare_outputs: Query<OutputFilenames>,
-    global_ctxt: Query<QueryContext<'tcx>>,
-    ongoing_codegen: Query<Box<dyn Any>>,
+    // This just points to what's in `gcx_cell`.
+    gcx: Query<&'tcx GlobalCtxt<'tcx>>,
 }
 
 impl<'tcx> Queries<'tcx> {
     pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> {
         Queries {
             compiler,
-            gcx: OnceCell::new(),
-            queries: OnceCell::new(),
+            gcx_cell: OnceCell::new(),
             arena: WorkerLocal::new(|_| Arena::default()),
-            hir_arena: WorkerLocal::new(|_| rustc_ast_lowering::Arena::default()),
+            hir_arena: WorkerLocal::new(|_| rustc_hir::Arena::default()),
             dep_graph_future: Default::default(),
             parse: Default::default(),
+            pre_configure: Default::default(),
             crate_name: Default::default(),
             register_plugins: Default::default(),
-            expansion: Default::default(),
             dep_graph: Default::default(),
-            prepare_outputs: Default::default(),
-            global_ctxt: Default::default(),
-            ongoing_codegen: Default::default(),
+            gcx: Default::default(),
         }
     }
 
     fn session(&self) -> &Lrc<Session> {
         &self.compiler.sess
     }
-    fn codegen_backend(&self) -> &Lrc<Box<dyn CodegenBackend>> {
+    fn codegen_backend(&self) -> &Lrc<dyn CodegenBackend> {
         self.compiler.codegen_backend()
     }
 
-    fn dep_graph_future(&self) -> Result<&Query<Option<DepGraphFuture>>> {
+    fn dep_graph_future(&self) -> Result<QueryResult<'_, Option<DepGraphFuture>>> {
         self.dep_graph_future.compute(|| {
             let sess = self.session();
             Ok(sess.opts.build_dep_graph().then(|| rustc_incremental::load_dep_graph(sess)))
         })
     }
 
-    pub fn parse(&self) -> Result<&Query<ast::Crate>> {
-        self.parse.compute(|| {
-            passes::parse(self.session(), &self.compiler.input)
-                .map_err(|mut parse_error| parse_error.emit())
+    pub fn parse(&self) -> Result<QueryResult<'_, ast::Crate>> {
+        self.parse
+            .compute(|| passes::parse(self.session()).map_err(|mut parse_error| parse_error.emit()))
+    }
+
+    pub fn pre_configure(&self) -> Result<QueryResult<'_, (ast::Crate, ast::AttrVec)>> {
+        self.pre_configure.compute(|| {
+            let mut krate = self.parse()?.steal();
+
+            let sess = self.session();
+            rustc_builtin_macros::cmdline_attrs::inject(
+                &mut krate,
+                &sess.parse_sess,
+                &sess.opts.unstable_opts.crate_attr,
+            );
+
+            let pre_configured_attrs =
+                rustc_expand::config::pre_configure_attrs(sess, &krate.attrs);
+            Ok((krate, pre_configured_attrs))
         })
     }
 
-    pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, Lrc<LintStore>)>> {
+    pub fn register_plugins(
+        &self,
+    ) -> Result<QueryResult<'_, (ast::Crate, ast::AttrVec, Lrc<LintStore>)>> {
         self.register_plugins.compute(|| {
-            let crate_name = self.crate_name()?.peek().clone();
-            let krate = self.parse()?.take();
+            let crate_name = *self.crate_name()?.borrow();
+            let (krate, pre_configured_attrs) = self.pre_configure()?.steal();
 
             let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {};
-            let (krate, lint_store) = passes::register_plugins(
+            let lint_store = passes::register_plugins(
                 self.session(),
                 &*self.codegen_backend().metadata_loader(),
                 self.compiler.register_lints.as_deref().unwrap_or_else(|| empty),
-                krate,
-                &crate_name,
+                &pre_configured_attrs,
+                crate_name,
             )?;
 
             // Compute the dependency graph (in the background). We want to do
@@ -147,52 +171,36 @@ impl<'tcx> Queries<'tcx> {
             // called, which happens within passes::register_plugins().
             self.dep_graph_future().ok();
 
-            Ok((krate, Lrc::new(lint_store)))
+            Ok((krate, pre_configured_attrs, Lrc::new(lint_store)))
         })
     }
 
-    pub fn crate_name(&self) -> Result<&Query<String>> {
+    fn crate_name(&self) -> Result<QueryResult<'_, Symbol>> {
         self.crate_name.compute(|| {
             Ok({
-                let parse_result = self.parse()?;
-                let krate = parse_result.peek();
+                let pre_configure_result = self.pre_configure()?;
+                let (_, pre_configured_attrs) = &*pre_configure_result.borrow();
                 // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches.
-                find_crate_name(self.session(), &krate.attrs, &self.compiler.input)
+                find_crate_name(self.session(), pre_configured_attrs)
             })
         })
     }
 
-    pub fn expansion(
-        &self,
-    ) -> Result<&Query<(Rc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>> {
-        tracing::trace!("expansion");
-        self.expansion.compute(|| {
-            let crate_name = self.crate_name()?.peek().clone();
-            let (krate, lint_store) = self.register_plugins()?.take();
-            let _timer = self.session().timer("configure_and_expand");
-            let sess = self.session();
-            let mut resolver = passes::create_resolver(
-                sess.clone(),
-                self.codegen_backend().metadata_loader(),
-                &krate,
-                &crate_name,
-            );
-            let krate = resolver.access(|resolver| {
-                passes::configure_and_expand(sess, &lint_store, krate, &crate_name, resolver)
-            })?;
-            Ok((Rc::new(krate), Rc::new(RefCell::new(resolver)), lint_store))
-        })
-    }
-
-    fn dep_graph(&self) -> Result<&Query<DepGraph>> {
+    fn dep_graph(&self) -> Result<QueryResult<'_, DepGraph>> {
         self.dep_graph.compute(|| {
             let sess = self.session();
-            let future_opt = self.dep_graph_future()?.take();
+            let future_opt = self.dep_graph_future()?.steal();
             let dep_graph = future_opt
                 .and_then(|future| {
-                    let (prev_graph, prev_work_products) =
+                    let (prev_graph, mut prev_work_products) =
                         sess.time("blocked_on_dep_graph_loading", || future.open().open(sess));
-
+                    // Convert from UnordMap to FxIndexMap by sorting
+                    let prev_work_product_ids =
+                        prev_work_products.items().map(|x| *x.0).into_sorted_stable_ord();
+                    let prev_work_products = prev_work_product_ids
+                        .into_iter()
+                        .map(|x| (x, prev_work_products.remove(&x).unwrap()))
+                        .collect::<FxIndexMap<_, _>>();
                     rustc_incremental::build_dep_graph(sess, prev_graph, prev_work_products)
                 })
                 .unwrap_or_else(DepGraph::new_disabled);
@@ -200,56 +208,58 @@ impl<'tcx> Queries<'tcx> {
         })
     }
 
-    pub fn prepare_outputs(&self) -> Result<&Query<OutputFilenames>> {
-        self.prepare_outputs.compute(|| {
-            let (krate, boxed_resolver, _) = &*self.expansion()?.peek();
-            let crate_name = self.crate_name()?.peek();
-            passes::prepare_outputs(
-                self.session(),
-                self.compiler,
-                krate,
-                &*boxed_resolver,
-                &crate_name,
-            )
-        })
-    }
+    pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, &'tcx GlobalCtxt<'tcx>>> {
+        self.gcx.compute(|| {
+            let crate_name = *self.crate_name()?.borrow();
+            let (krate, pre_configured_attrs, lint_store) = self.register_plugins()?.steal();
 
-    pub fn global_ctxt(&'tcx self) -> Result<&Query<QueryContext<'tcx>>> {
-        self.global_ctxt.compute(|| {
-            let crate_name = self.crate_name()?.peek().clone();
-            let outputs = self.prepare_outputs()?.peek().clone();
-            let dep_graph = self.dep_graph()?.peek().clone();
-            let (krate, resolver, lint_store) = self.expansion()?.take();
-            Ok(passes::create_global_ctxt(
+            let sess = self.session();
+
+            let cstore = RwLock::new(Box::new(CStore::new(sess)) as _);
+            let definitions = RwLock::new(Definitions::new(sess.local_stable_crate_id()));
+            let source_span = AppendOnlyIndexVec::new();
+            let _id = source_span.push(krate.spans.inner_span);
+            debug_assert_eq!(_id, CRATE_DEF_ID);
+            let untracked = Untracked { cstore, source_span, definitions };
+
+            let qcx = passes::create_global_ctxt(
                 self.compiler,
                 lint_store,
-                krate,
-                dep_graph,
-                resolver,
-                outputs,
-                &crate_name,
-                &self.queries,
-                &self.gcx,
+                self.dep_graph()?.steal(),
+                untracked,
+                &self.gcx_cell,
                 &self.arena,
                 &self.hir_arena,
-            ))
+            );
+
+            qcx.enter(|tcx| {
+                let feed = tcx.feed_local_crate();
+                feed.crate_name(crate_name);
+
+                let feed = tcx.feed_unit_query();
+                feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs))));
+                feed.metadata_loader(
+                    tcx.arena.alloc(Steal::new(self.codegen_backend().metadata_loader())),
+                );
+                feed.features_query(tcx.sess.features_untracked());
+            });
+            Ok(qcx)
         })
     }
 
-    pub fn ongoing_codegen(&'tcx self) -> Result<&Query<Box<dyn Any>>> {
-        self.ongoing_codegen.compute(|| {
-            let outputs = self.prepare_outputs()?;
-            self.global_ctxt()?.peek_mut().enter(|tcx| {
-                tcx.analysis(()).ok();
+    pub fn ongoing_codegen(&'tcx self) -> Result<Box<dyn Any>> {
+        self.global_ctxt()?.enter(|tcx| {
+            // Don't do code generation if there were any errors
+            self.session().compile_status()?;
 
-                // Don't do code generation if there were any errors
-                self.session().compile_status()?;
+            // If we have any delayed bugs, for example because we created TyKind::Error earlier,
+            // it's likely that codegen will only cause more ICEs, obscuring the original problem
+            self.session().diagnostic().flush_delayed();
 
-                // Hook for UI tests.
-                Self::check_for_rustc_errors_attr(tcx);
+            // Hook for UI tests.
+            Self::check_for_rustc_errors_attr(tcx);
 
-                Ok(passes::start_codegen(&***self.codegen_backend(), tcx, &*outputs.peek()))
-            })
+            Ok(passes::start_codegen(&**self.codegen_backend(), tcx))
         })
     }
 
@@ -274,31 +284,30 @@ impl<'tcx> Queries<'tcx> {
 
                 // Bare `#[rustc_error]`.
                 None => {
-                    tcx.sess.span_fatal(
-                        tcx.def_span(def_id),
-                        "fatal error triggered by #[rustc_error]",
-                    );
+                    tcx.sess.emit_fatal(RustcErrorFatal { span: tcx.def_span(def_id) });
                 }
 
                 // Some other attribute.
                 Some(_) => {
-                    tcx.sess.span_warn(
-                        tcx.def_span(def_id),
-                        "unexpected annotation used with `#[rustc_error(...)]!",
-                    );
+                    tcx.sess.emit_warning(RustcErrorUnexpectedAnnotation {
+                        span: tcx.def_span(def_id),
+                    });
                 }
             }
         }
     }
 
-    pub fn linker(&'tcx self) -> Result<Linker> {
+    pub fn linker(&'tcx self, ongoing_codegen: Box<dyn Any>) -> Result<Linker> {
         let sess = self.session().clone();
         let codegen_backend = self.codegen_backend().clone();
 
-        let dep_graph = self.dep_graph()?.peek().clone();
-        let prepare_outputs = self.prepare_outputs()?.take();
-        let crate_hash = self.global_ctxt()?.peek_mut().enter(|tcx| tcx.crate_hash(LOCAL_CRATE));
-        let ongoing_codegen = self.ongoing_codegen()?.take();
+        let (crate_hash, prepare_outputs, dep_graph) = self.global_ctxt()?.enter(|tcx| {
+            (
+                if tcx.sess.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None },
+                tcx.output_filenames(()).clone(),
+                tcx.dep_graph.clone(),
+            )
+        });
 
         Ok(Linker {
             sess,
@@ -315,12 +324,13 @@ impl<'tcx> Queries<'tcx> {
 pub struct Linker {
     // compilation inputs
     sess: Lrc<Session>,
-    codegen_backend: Lrc<Box<dyn CodegenBackend>>,
+    codegen_backend: Lrc<dyn CodegenBackend>,
 
     // compilation outputs
     dep_graph: DepGraph,
-    prepare_outputs: OutputFilenames,
-    crate_hash: Svh,
+    prepare_outputs: Arc<OutputFilenames>,
+    // Only present when incr. comp. is enabled.
+    crate_hash: Option<Svh>,
     ongoing_codegen: Box<dyn Any>,
 }
 
@@ -357,12 +367,10 @@ impl Linker {
             return Ok(());
         }
 
-        if sess.opts.debugging_opts.no_link {
-            let encoded = CodegenResults::serialize_rlink(&codegen_results);
+        if sess.opts.unstable_opts.no_link {
             let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT);
-            std::fs::write(&rlink_file, encoded).map_err(|err| {
-                sess.fatal(&format!("failed to write file {}: {}", rlink_file.display(), err));
-            })?;
+            CodegenResults::serialize_rlink(sess, &rlink_file, &codegen_results)
+                .map_err(|error| sess.emit_fatal(FailedWritingFile { path: &rlink_file, error }))?;
             return Ok(());
         }
 
@@ -382,7 +390,8 @@ impl Compiler {
 
         // NOTE: intentionally does not compute the global context if it hasn't been built yet,
         // since that likely means there was a parse error.
-        if let Some(Ok(gcx)) = &mut *queries.global_ctxt.result.borrow_mut() {
+        if let Some(Ok(gcx)) = &mut *queries.gcx.result.borrow_mut() {
+            let gcx = gcx.get_mut();
             // We assume that no queries are run past here. If there are new queries
             // after this point, they'll show up as "<unknown>" in self-profiling data.
             {
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 30a29ed6ed3..e3d66d18388 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -1,55 +1,73 @@
+#![allow(rustc::bad_opt_access)]
 use crate::interface::parse_cfgspecs;
 
 use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::profiling::TimePassesFormat;
 use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
-use rustc_session::config::InstrumentCoverage;
-use rustc_session::config::Strip;
+use rustc_session::config::rustc_optgroups;
+use rustc_session::config::DebugInfo;
+use rustc_session::config::Input;
+use rustc_session::config::InstrumentXRay;
+use rustc_session::config::LinkSelfContained;
+use rustc_session::config::TraitSolver;
 use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
 use rustc_session::config::{
-    rustc_optgroups, ErrorOutputType, ExternLocation, LocationDetail, Options, Passes,
-};
-use rustc_session::config::{
-    BranchProtection, Externs, OomStrategy, OutputType, OutputTypes, PAuthKey, PacRet,
-    SymbolManglingVersion, WasiExecModel,
+    BranchProtection, Externs, OomStrategy, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet,
+    ProcMacroExecutionStrategy, SymbolManglingVersion, WasiExecModel,
 };
 use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
+use rustc_session::config::{DumpMonoStatsFormat, MirSpanview};
+use rustc_session::config::{ErrorOutputType, ExternLocation, LocationDetail, Options, Strip};
+use rustc_session::config::{InstrumentCoverage, Passes};
 use rustc_session::lint::Level;
 use rustc_session::search_paths::SearchPath;
 use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
-use rustc_session::{build_session, getopts, DiagnosticOutput, Session};
+use rustc_session::{build_session, getopts, Session};
+use rustc_session::{CompilerIO, EarlyErrorHandler};
 use rustc_span::edition::{Edition, DEFAULT_EDITION};
 use rustc_span::symbol::sym;
+use rustc_span::FileName;
 use rustc_span::SourceFileHashAlgorithm;
-use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy};
-use rustc_target::spec::{
-    RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel,
-};
+use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel};
+use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel};
 
 use std::collections::{BTreeMap, BTreeSet};
-use std::iter::FromIterator;
 use std::num::NonZeroUsize;
 use std::path::{Path, PathBuf};
 
 type CfgSpecs = FxHashSet<(String, Option<String>)>;
 
-fn build_session_options_and_crate_config(matches: getopts::Matches) -> (Options, CfgSpecs) {
-    let sessopts = build_session_options(&matches);
-    let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
+fn build_session_options_and_crate_config(
+    handler: &mut EarlyErrorHandler,
+    matches: getopts::Matches,
+) -> (Options, CfgSpecs) {
+    let sessopts = build_session_options(handler, &matches);
+    let cfg = parse_cfgspecs(handler, matches.opt_strs("cfg"));
     (sessopts, cfg)
 }
 
-fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
+fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Session, CfgSpecs) {
     let registry = registry::Registry::new(&[]);
-    let (sessopts, cfg) = build_session_options_and_crate_config(matches);
+    let (sessopts, cfg) = build_session_options_and_crate_config(handler, matches);
+    let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
+    let io = CompilerIO {
+        input: Input::Str { name: FileName::Custom(String::new()), input: String::new() },
+        output_dir: None,
+        output_file: None,
+        temps_dir,
+    };
     let sess = build_session(
+        handler,
         sessopts,
-        None,
+        io,
         None,
         registry,
-        DiagnosticOutput::Default,
+        vec![],
         Default::default(),
         None,
         None,
+        "",
+        None,
     );
     (sess, cfg)
 }
@@ -67,6 +85,7 @@ where
         is_private_dep: false,
         add_prelude: true,
         nounused_dep: false,
+        force: false,
     }
 }
 
@@ -116,7 +135,8 @@ fn assert_non_crate_hash_different(x: &Options, y: &Options) {
 fn test_switch_implies_cfg_test() {
     rustc_span::create_default_session_globals_then(|| {
         let matches = optgroups().parse(&["--test".to_string()]).unwrap();
-        let (sess, cfg) = mk_session(matches);
+        let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+        let (sess, cfg) = mk_session(&mut handler, matches);
         let cfg = build_configuration(&sess, to_crate_config(cfg));
         assert!(cfg.contains(&(sym::test, None)));
     });
@@ -127,7 +147,8 @@ fn test_switch_implies_cfg_test() {
 fn test_switch_implies_cfg_test_unless_cfg_test() {
     rustc_span::create_default_session_globals_then(|| {
         let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap();
-        let (sess, cfg) = mk_session(matches);
+        let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+        let (sess, cfg) = mk_session(&mut handler, matches);
         let cfg = build_configuration(&sess, to_crate_config(cfg));
         let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test);
         assert!(test_items.next().is_some());
@@ -139,20 +160,23 @@ fn test_switch_implies_cfg_test_unless_cfg_test() {
 fn test_can_print_warnings() {
     rustc_span::create_default_session_globals_then(|| {
         let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap();
-        let (sess, _) = mk_session(matches);
+        let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+        let (sess, _) = mk_session(&mut handler, matches);
         assert!(!sess.diagnostic().can_emit_warnings());
     });
 
     rustc_span::create_default_session_globals_then(|| {
         let matches =
             optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap();
-        let (sess, _) = mk_session(matches);
+        let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+        let (sess, _) = mk_session(&mut handler, matches);
         assert!(sess.diagnostic().can_emit_warnings());
     });
 
     rustc_span::create_default_session_globals_then(|| {
         let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap();
-        let (sess, _) = mk_session(matches);
+        let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+        let (sess, _) = mk_session(&mut handler, matches);
         assert!(sess.diagnostic().can_emit_warnings());
     });
 }
@@ -163,8 +187,14 @@ fn test_output_types_tracking_hash_different_paths() {
     let mut v2 = Options::default();
     let mut v3 = Options::default();
 
-    v1.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("./some/thing")))]);
-    v2.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("/some/thing")))]);
+    v1.output_types = OutputTypes::new(&[(
+        OutputType::Exe,
+        Some(OutFileName::Real(PathBuf::from("./some/thing"))),
+    )]);
+    v2.output_types = OutputTypes::new(&[(
+        OutputType::Exe,
+        Some(OutFileName::Real(PathBuf::from("/some/thing"))),
+    )]);
     v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]);
 
     assert_non_crate_hash_different(&v1, &v2);
@@ -178,13 +208,13 @@ fn test_output_types_tracking_hash_different_construction_order() {
     let mut v2 = Options::default();
 
     v1.output_types = OutputTypes::new(&[
-        (OutputType::Exe, Some(PathBuf::from("./some/thing"))),
-        (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))),
+        (OutputType::Exe, Some(OutFileName::Real(PathBuf::from("./some/thing")))),
+        (OutputType::Bitcode, Some(OutFileName::Real(PathBuf::from("./some/thing.bc")))),
     ]);
 
     v2.output_types = OutputTypes::new(&[
-        (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))),
-        (OutputType::Exe, Some(PathBuf::from("./some/thing"))),
+        (OutputType::Bitcode, Some(OutFileName::Real(PathBuf::from("./some/thing.bc")))),
+        (OutputType::Exe, Some(OutFileName::Real(PathBuf::from("./some/thing")))),
     ]);
 
     assert_same_hash(&v1, &v2);
@@ -292,35 +322,36 @@ fn test_search_paths_tracking_hash_different_order() {
     let mut v3 = Options::default();
     let mut v4 = Options::default();
 
+    let handler = EarlyErrorHandler::new(JSON);
     const JSON: ErrorOutputType = ErrorOutputType::Json {
         pretty: false,
         json_rendered: HumanReadableErrorType::Default(ColorConfig::Never),
     };
 
     // Reference
-    v1.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
-    v1.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
-    v1.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
-    v1.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
-    v1.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
-
-    v2.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
-    v2.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
-    v2.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
-    v2.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
-    v2.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
-
-    v3.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
-    v3.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
-    v3.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
-    v3.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
-    v3.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
-
-    v4.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
-    v4.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
-    v4.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
-    v4.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
-    v4.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
+    v1.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
+    v1.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
+    v1.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
+    v1.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
+    v1.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
+
+    v2.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
+    v2.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
+    v2.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
+    v2.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
+    v2.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
+
+    v3.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
+    v3.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
+    v3.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
+    v3.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
+    v3.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
+
+    v4.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
+    v4.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
+    v4.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
+    v4.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
+    v4.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
 
     assert_same_hash(&v1, &v2);
     assert_same_hash(&v1, &v3);
@@ -541,22 +572,24 @@ fn test_codegen_options_tracking_hash() {
     }
 
     // Make sure that changing an [UNTRACKED] option leaves the hash unchanged.
-    // This list is in alphabetical order.
+    // tidy-alphabetical-start
     untracked!(ar, String::from("abc"));
     untracked!(codegen_units, Some(42));
     untracked!(default_linker_libraries, true);
+    untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe")));
     untracked!(extra_filename, String::from("extra-filename"));
     untracked!(incremental, Some(String::from("abc")));
     // `link_arg` is omitted because it just forwards to `link_args`.
     untracked!(link_args, vec![String::from("abc"), String::from("def")]);
-    untracked!(link_self_contained, Some(true));
+    untracked!(link_self_contained, LinkSelfContained::on());
     untracked!(linker, Some(PathBuf::from("linker")));
-    untracked!(linker_flavor, Some(LinkerFlavor::Gcc));
+    untracked!(linker_flavor, Some(LinkerFlavorCli::Gcc));
     untracked!(no_stack_check, true);
     untracked!(remark, Passes::Some(vec![String::from("pass1"), String::from("pass2")]));
     untracked!(rpath, true);
     untracked!(save_temps, true);
     untracked!(strip, Strip::Debuginfo);
+    // tidy-alphabetical-end
 
     macro_rules! tracked {
         ($name: ident, $non_default_value: expr) => {
@@ -568,18 +601,18 @@ fn test_codegen_options_tracking_hash() {
     }
 
     // Make sure that changing a [TRACKED] option changes the hash.
-    // This list is in alphabetical order.
+    // tidy-alphabetical-start
     tracked!(code_model, Some(CodeModel::Large));
     tracked!(control_flow_guard, CFGuard::Checks);
     tracked!(debug_assertions, Some(true));
-    tracked!(debuginfo, 0xdeadbeef);
+    tracked!(debuginfo, DebugInfo::Limited);
     tracked!(embed_bitcode, false);
     tracked!(force_frame_pointers, Some(false));
     tracked!(force_unwind_tables, Some(true));
     tracked!(inline_threshold, Some(0xf007ba11));
     tracked!(instrument_coverage, Some(InstrumentCoverage::All));
-    tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
     tracked!(link_dead_code, Some(true));
+    tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
     tracked!(llvm_args, vec![String::from("1"), String::from("2")]);
     tracked!(lto, LtoCli::Fat);
     tracked!(metadata, vec![String::from("A"), String::from("B")]);
@@ -600,6 +633,7 @@ fn test_codegen_options_tracking_hash() {
     tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0));
     tracked!(target_cpu, Some(String::from("abc")));
     tracked!(target_feature, String::from("all the features, all of them"));
+    // tidy-alphabetical-end
 }
 
 #[test]
@@ -620,45 +654,49 @@ fn test_top_level_options_tracked_no_crate() {
     }
 
     // Make sure that changing a [TRACKED_NO_CRATE_HASH] option leaves the crate hash unchanged but changes the incremental hash.
-    // This list is in alphabetical order.
-    tracked!(remap_path_prefix, vec![("/home/bors/rust".into(), "src".into())]);
+    // tidy-alphabetical-start
     tracked!(
         real_rust_source_base_dir,
         Some("/home/bors/rust/.rustup/toolchains/nightly/lib/rustlib/src/rust".into())
     );
+    tracked!(remap_path_prefix, vec![("/home/bors/rust".into(), "src".into())]);
+    // tidy-alphabetical-end
 }
 
 #[test]
-fn test_debugging_options_tracking_hash() {
+fn test_unstable_options_tracking_hash() {
     let reference = Options::default();
     let mut opts = Options::default();
 
     macro_rules! untracked {
         ($name: ident, $non_default_value: expr) => {
-            assert_ne!(opts.debugging_opts.$name, $non_default_value);
-            opts.debugging_opts.$name = $non_default_value;
+            assert_ne!(opts.unstable_opts.$name, $non_default_value);
+            opts.unstable_opts.$name = $non_default_value;
             assert_same_hash(&reference, &opts);
         };
     }
 
     // Make sure that changing an [UNTRACKED] option leaves the hash unchanged.
-    // This list is in alphabetical order.
+    // tidy-alphabetical-start
     untracked!(assert_incr_state, Some(String::from("loaded")));
     untracked!(deduplicate_diagnostics, false);
     untracked!(dep_tasks, true);
-    untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe")));
     untracked!(dont_buffer_diagnostics, true);
     untracked!(dump_dep_graph, true);
+    untracked!(dump_drop_tracking_cfg, Some("cfg.dot".to_string()));
     untracked!(dump_mir, Some(String::from("abc")));
     untracked!(dump_mir_dataflow, true);
     untracked!(dump_mir_dir, String::from("abc"));
     untracked!(dump_mir_exclude_pass_number, true);
     untracked!(dump_mir_graphviz, true);
+    untracked!(dump_mir_spanview, Some(MirSpanview::Statement));
+    untracked!(dump_mono_stats, SwitchWithOptPath::Enabled(Some("mono-items-dir/".into())));
+    untracked!(dump_mono_stats_format, DumpMonoStatsFormat::Json);
+    untracked!(dylib_lto, true);
     untracked!(emit_stack_sizes, true);
     untracked!(future_incompat_test, true);
     untracked!(hir_stats, true);
     untracked!(identify_regions, true);
-    untracked!(incremental_ignore_spans, true);
     untracked!(incremental_info, true);
     untracked!(incremental_verify_ich, true);
     untracked!(input_stats, true);
@@ -668,56 +706,60 @@ fn test_debugging_options_tracking_hash() {
     untracked!(ls, true);
     untracked!(macro_backtrace, true);
     untracked!(meta_stats, true);
+    untracked!(mir_include_spans, true);
     untracked!(nll_facts, true);
     untracked!(no_analysis, true);
-    untracked!(no_interleave_lints, true);
     untracked!(no_leak_check, true);
     untracked!(no_parallel_llvm, true);
     untracked!(parse_only, true);
     untracked!(perf_stats, true);
     // `pre_link_arg` is omitted because it just forwards to `pre_link_args`.
     untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]);
-    untracked!(profile_closures, true);
+    untracked!(print_codegen_stats, true);
     untracked!(print_llvm_passes, true);
     untracked!(print_mono_items, Some(String::from("abc")));
     untracked!(print_type_sizes, true);
     untracked!(proc_macro_backtrace, true);
+    untracked!(proc_macro_execution_strategy, ProcMacroExecutionStrategy::CrossThread);
+    untracked!(profile_closures, true);
     untracked!(query_dep_graph, true);
-    untracked!(save_analysis, true);
     untracked!(self_profile, SwitchWithOptPath::Enabled(None));
     untracked!(self_profile_events, Some(vec![String::new()]));
     untracked!(span_debug, true);
     untracked!(span_free_formats, true);
     untracked!(temps_dir, Some(String::from("abc")));
-    untracked!(terminal_width, Some(80));
     untracked!(threads, 99);
-    untracked!(time, true);
     untracked!(time_llvm_passes, true);
     untracked!(time_passes, true);
+    untracked!(time_passes_format, TimePassesFormat::Json);
     untracked!(trace_macros, true);
+    untracked!(track_diagnostics, true);
     untracked!(trim_diagnostic_paths, false);
     untracked!(ui_testing, true);
     untracked!(unpretty, Some("expanded".to_string()));
     untracked!(unstable_options, true);
     untracked!(validate_mir, true);
     untracked!(verbose, true);
+    untracked!(write_long_types_to_disk, false);
+    // tidy-alphabetical-end
 
     macro_rules! tracked {
         ($name: ident, $non_default_value: expr) => {
             opts = reference.clone();
-            assert_ne!(opts.debugging_opts.$name, $non_default_value);
-            opts.debugging_opts.$name = $non_default_value;
+            assert_ne!(opts.unstable_opts.$name, $non_default_value);
+            opts.unstable_opts.$name = $non_default_value;
             assert_different_hash(&reference, &opts);
         };
     }
 
     // Make sure that changing a [TRACKED] option changes the hash.
-    // This list is in alphabetical order.
+    // tidy-alphabetical-start
     tracked!(allow_features, Some(vec![String::from("lang_items")]));
     tracked!(always_encode_mir, true);
     tracked!(asm_comments, true);
     tracked!(assume_incomplete_release, true);
     tracked!(binary_dep_depinfo, true);
+    tracked!(box_noalias, false);
     tracked!(
         branch_protection,
         Some(BranchProtection {
@@ -725,7 +767,6 @@ fn test_debugging_options_tracking_hash() {
             pac_ret: Some(PacRet { leaf: true, key: PAuthKey::B })
         })
     );
-    tracked!(chalk, true);
     tracked!(codegen_backend, Some("abc".to_string()));
     tracked!(crate_attr, vec!["abc".to_string()]);
     tracked!(debug_info_for_profiling, true);
@@ -733,62 +774,77 @@ fn test_debugging_options_tracking_hash() {
     tracked!(dep_info_omit_d_target, true);
     tracked!(drop_tracking, true);
     tracked!(dual_proc_macros, true);
+    tracked!(dwarf_version, Some(5));
+    tracked!(emit_thin_lto, false);
+    tracked!(export_executable_symbols, true);
     tracked!(fewer_names, Some(true));
+    tracked!(flatten_format_args, false);
     tracked!(force_unstable_if_unmarked, true);
     tracked!(fuel, Some(("abc".to_string(), 99)));
     tracked!(function_sections, Some(false));
     tracked!(human_readable_cgu_names, true);
+    tracked!(incremental_ignore_spans, true);
     tracked!(inline_in_all_cgus, Some(true));
     tracked!(inline_mir, Some(true));
     tracked!(inline_mir_hint_threshold, Some(123));
     tracked!(inline_mir_threshold, Some(123));
     tracked!(instrument_coverage, Some(InstrumentCoverage::All));
     tracked!(instrument_mcount, true);
+    tracked!(instrument_xray, Some(InstrumentXRay::default()));
+    tracked!(link_directives, false);
     tracked!(link_only, true);
     tracked!(llvm_plugins, vec![String::from("plugin_name")]);
     tracked!(location_detail, LocationDetail { file: true, line: false, column: false });
+    tracked!(maximal_hir_to_mir_coverage, true);
     tracked!(merge_functions, Some(MergeFunctions::Disabled));
     tracked!(mir_emit_retag, true);
     tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]);
+    tracked!(mir_keep_place_mention, true);
     tracked!(mir_opt_level, Some(4));
     tracked!(move_size_limit, Some(4096));
-    tracked!(mutable_noalias, Some(true));
-    tracked!(new_llvm_pass_manager, Some(true));
+    tracked!(mutable_noalias, false);
     tracked!(no_generate_arange_section, true);
+    tracked!(no_jump_tables, true);
     tracked!(no_link, true);
-    tracked!(no_unique_section_names, true);
     tracked!(no_profiler_runtime, true);
+    tracked!(no_unique_section_names, true);
     tracked!(oom, OomStrategy::Panic);
     tracked!(osx_rpath_install_name, true);
+    tracked!(packed_bundled_libs, true);
     tracked!(panic_abort_tests, true);
     tracked!(panic_in_drop, PanicStrategy::Abort);
-    tracked!(pick_stable_methods_before_any_unstable, false);
     tracked!(plt, Some(true));
     tracked!(polonius, true);
     tracked!(precise_enum_drop_elaboration, false);
     tracked!(print_fuel, Some("abc".to_string()));
     tracked!(profile, true);
     tracked!(profile_emit, Some(PathBuf::from("abc")));
-    tracked!(profiler_runtime, "abc".to_string());
     tracked!(profile_sample_use, Some(PathBuf::from("abc")));
+    tracked!(profiler_runtime, "abc".to_string());
     tracked!(relax_elf_relocations, Some(true));
     tracked!(relro_level, Some(RelroLevel::Full));
     tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
     tracked!(report_delayed_bugs, true);
     tracked!(sanitizer, SanitizerSet::ADDRESS);
+    tracked!(sanitizer_cfi_canonical_jump_tables, None);
+    tracked!(sanitizer_cfi_generalize_pointers, Some(true));
+    tracked!(sanitizer_cfi_normalize_integers, Some(true));
     tracked!(sanitizer_memory_track_origins, 2);
     tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
     tracked!(saturating_float_casts, Some(true));
     tracked!(share_generics, Some(true));
     tracked!(show_span, Some(String::from("abc")));
     tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc")));
+    tracked!(split_lto_unit, Some(true));
     tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
     tracked!(stack_protector, StackProtector::All);
     tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0));
     tracked!(teach, true);
     tracked!(thinlto, Some(true));
     tracked!(thir_unsafeck, true);
+    tracked!(tiny_const_eval_limit, true);
     tracked!(tls_model, Some(TlsModel::GeneralDynamic));
+    tracked!(trait_solver, TraitSolver::NextCoherence);
     tracked!(translate_remapped_path_to_local_path, false);
     tracked!(trap_unreachable, Some(false));
     tracked!(treat_err_as_bug, NonZeroUsize::new(1));
@@ -799,12 +855,13 @@ fn test_debugging_options_tracking_hash() {
     tracked!(verify_llvm_ir, true);
     tracked!(virtual_function_elimination, true);
     tracked!(wasi_exec_model, Some(WasiExecModel::Reactor));
+    // tidy-alphabetical-end
 
     macro_rules! tracked_no_crate_hash {
         ($name: ident, $non_default_value: expr) => {
             opts = reference.clone();
-            assert_ne!(opts.debugging_opts.$name, $non_default_value);
-            opts.debugging_opts.$name = $non_default_value;
+            assert_ne!(opts.unstable_opts.$name, $non_default_value);
+            opts.unstable_opts.$name = $non_default_value;
             assert_non_crate_hash_different(&reference, &opts);
         };
     }
@@ -817,7 +874,9 @@ fn test_edition_parsing() {
     let options = Options::default();
     assert!(options.edition == DEFAULT_EDITION);
 
+    let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+
     let matches = optgroups().parse(&["--edition=2018".to_string()]).unwrap();
-    let (sessopts, _) = build_session_options_and_crate_config(matches);
+    let (sessopts, _) = build_session_options_and_crate_config(&mut handler, matches);
     assert!(sessopts.edition == Edition::Edition2018)
 }
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index fb9258eb4a9..ad35dbbc8f9 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -1,37 +1,33 @@
+use crate::errors;
+use info;
 use libloading::Library;
 use rustc_ast as ast;
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 #[cfg(parallel_compiler)]
-use rustc_data_structures::jobserver;
-use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::sync;
 use rustc_errors::registry::Registry;
-#[cfg(parallel_compiler)]
-use rustc_middle::ty::tls;
 use rustc_parse::validate_attr;
-#[cfg(parallel_compiler)]
-use rustc_query_impl::QueryCtxt;
 use rustc_session as session;
 use rustc_session::config::CheckCfg;
 use rustc_session::config::{self, CrateType};
-use rustc_session::config::{ErrorOutputType, Input, OutputFilenames};
+use rustc_session::config::{OutFileName, OutputFilenames, OutputTypes};
+use rustc_session::filesearch::sysroot_candidates;
 use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
 use rustc_session::parse::CrateConfig;
-use rustc_session::{early_error, filesearch, output, DiagnosticOutput, Session};
+use rustc_session::{filesearch, output, Session};
+use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
-use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::source_map::FileLoader;
 use rustc_span::symbol::{sym, Symbol};
+use session::{CompilerIO, EarlyErrorHandler};
 use std::env;
 use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
 use std::mem;
-#[cfg(not(parallel_compiler))]
-use std::panic;
 use std::path::{Path, PathBuf};
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::OnceLock;
 use std::thread;
-use tracing::info;
 
 /// Function pointer type that constructs a new CodegenBackend.
 pub type MakeBackendFn = fn() -> Box<dyn CodegenBackend>;
@@ -40,7 +36,7 @@ pub type MakeBackendFn = fn() -> Box<dyn CodegenBackend>;
 /// specific features (SSE, NEON etc.).
 ///
 /// This is performed by checking whether a set of permitted features
-/// is available on the target machine, by querying LLVM.
+/// is available on the target machine, by querying the codegen backend.
 pub fn add_configuration(
     cfg: &mut CrateConfig,
     sess: &mut Session,
@@ -48,7 +44,10 @@ pub fn add_configuration(
 ) {
     let tf = sym::target_feature;
 
-    let target_features = codegen_backend.target_features(sess);
+    let unstable_target_features = codegen_backend.target_features(sess, true);
+    sess.unstable_target_features.extend(unstable_target_features.iter().cloned());
+
+    let target_features = codegen_backend.target_features(sess, false);
     sess.target_features.extend(target_features.iter().cloned());
 
     cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat))));
@@ -59,24 +58,27 @@ pub fn add_configuration(
 }
 
 pub fn create_session(
+    handler: &EarlyErrorHandler,
     sopts: config::Options,
     cfg: FxHashSet<(String, Option<String>)>,
     check_cfg: CheckCfg,
-    diagnostic_output: DiagnosticOutput,
+    locale_resources: &'static [&'static str],
     file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
-    input_path: Option<PathBuf>,
+    io: CompilerIO,
     lint_caps: FxHashMap<lint::LintId, lint::Level>,
     make_codegen_backend: Option<
         Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
     >,
     descriptions: Registry,
-) -> (Lrc<Session>, Lrc<Box<dyn CodegenBackend>>) {
+    ice_file: Option<PathBuf>,
+) -> (Session, Box<dyn CodegenBackend>) {
     let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend {
         make_codegen_backend(&sopts)
     } else {
         get_codegen_backend(
+            handler,
             &sopts.maybe_sysroot,
-            sopts.debugging_opts.codegen_backend.as_ref().map(|name| &name[..]),
+            sopts.unstable_opts.codegen_backend.as_deref(),
         )
     };
 
@@ -85,26 +87,32 @@ pub fn create_session(
 
     let bundle = match rustc_errors::fluent_bundle(
         sopts.maybe_sysroot.clone(),
-        sysroot_candidates(),
-        sopts.debugging_opts.translate_lang.clone(),
-        sopts.debugging_opts.translate_additional_ftl.as_deref(),
-        sopts.debugging_opts.translate_directionality_markers,
+        sysroot_candidates().to_vec(),
+        sopts.unstable_opts.translate_lang.clone(),
+        sopts.unstable_opts.translate_additional_ftl.as_deref(),
+        sopts.unstable_opts.translate_directionality_markers,
     ) {
         Ok(bundle) => bundle,
         Err(e) => {
-            early_error(sopts.error_format, &format!("failed to load fluent bundle: {e}"));
+            handler.early_error(format!("failed to load fluent bundle: {e}"));
         }
     };
 
+    let mut locale_resources = Vec::from(locale_resources);
+    locale_resources.push(codegen_backend.locale_resource());
+
     let mut sess = session::build_session(
+        handler,
         sopts,
-        input_path,
+        io,
         bundle,
         descriptions,
-        diagnostic_output,
+        locale_resources,
         lint_caps,
         file_loader,
         target_override,
+        rustc_version_str().unwrap_or("unknown"),
+        ice_file,
     );
 
     codegen_backend.init(&sess);
@@ -113,13 +121,12 @@ pub fn create_session(
     add_configuration(&mut cfg, &mut sess, &*codegen_backend);
 
     let mut check_cfg = config::to_crate_check_config(check_cfg);
-    check_cfg.fill_well_known();
-    check_cfg.fill_actual(&cfg);
+    check_cfg.fill_well_known(&sess.target);
 
     sess.parse_sess.config = cfg;
     sess.parse_sess.check_config = check_cfg;
 
-    (Lrc::new(sess), Lrc::new(codegen_backend))
+    (sess, codegen_backend)
 }
 
 const STACK_SIZE: usize = 8 * 1024 * 1024;
@@ -130,101 +137,105 @@ fn get_stack_size() -> Option<usize> {
     env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE)
 }
 
-/// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need
-/// for `'static` bounds.
-#[cfg(not(parallel_compiler))]
-fn scoped_thread<F: FnOnce() -> R + Send, R: Send>(cfg: thread::Builder, f: F) -> R {
-    // SAFETY: join() is called immediately, so any closure captures are still
-    // alive.
-    match unsafe { cfg.spawn_unchecked(f) }.unwrap().join() {
-        Ok(v) => v,
-        Err(e) => panic::resume_unwind(e),
-    }
-}
-
 #[cfg(not(parallel_compiler))]
-pub fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
+pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
     edition: Edition,
     _threads: usize,
     f: F,
 ) -> R {
-    let mut cfg = thread::Builder::new().name("rustc".to_string());
-
+    // The "thread pool" is a single spawned thread in the non-parallel
+    // compiler. We run on a spawned thread instead of the main thread (a) to
+    // provide control over the stack size, and (b) to increase similarity with
+    // the parallel compiler, in particular to ensure there is no accidental
+    // sharing of data between the main thread and the compilation thread
+    // (which might cause problems for the parallel compiler).
+    let mut builder = thread::Builder::new().name("rustc".to_string());
     if let Some(size) = get_stack_size() {
-        cfg = cfg.stack_size(size);
+        builder = builder.stack_size(size);
     }
 
-    let main_handler = move || rustc_span::create_session_globals_then(edition, f);
-
-    scoped_thread(cfg, main_handler)
-}
-
-/// Creates a new thread and forwards information in thread locals to it.
-/// The new thread runs the deadlock handler.
-/// Must only be called when a deadlock is about to happen.
-#[cfg(parallel_compiler)]
-unsafe fn handle_deadlock() {
-    let registry = rustc_rayon_core::Registry::current();
-
-    let context = tls::get_tlv();
-    assert!(context != 0);
-    rustc_data_structures::sync::assert_sync::<tls::ImplicitCtxt<'_, '_>>();
-    let icx: &tls::ImplicitCtxt<'_, '_> = &*(context as *const tls::ImplicitCtxt<'_, '_>);
-
-    let session_globals = rustc_span::with_session_globals(|sg| sg as *const _);
-    let session_globals = &*session_globals;
-    thread::spawn(move || {
-        tls::enter_context(icx, |_| {
-            rustc_span::set_session_globals_then(session_globals, || {
-                tls::with(|tcx| QueryCtxt::from_tcx(tcx).deadlock(&registry))
-            })
-        });
-    });
+    // We build the session globals and run `f` on the spawned thread, because
+    // `SessionGlobals` does not impl `Send` in the non-parallel compiler.
+    thread::scope(|s| {
+        // `unwrap` is ok here because `spawn_scoped` only panics if the thread
+        // name contains null bytes.
+        let r = builder
+            .spawn_scoped(s, move || rustc_span::create_session_globals_then(edition, f))
+            .unwrap()
+            .join();
+
+        match r {
+            Ok(v) => v,
+            Err(e) => std::panic::resume_unwind(e),
+        }
+    })
 }
 
 #[cfg(parallel_compiler)]
-pub fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
+pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
     edition: Edition,
     threads: usize,
     f: F,
 ) -> R {
-    let mut config = rayon::ThreadPoolBuilder::new()
+    use rustc_data_structures::jobserver;
+    use rustc_middle::ty::tls;
+    use rustc_query_impl::QueryCtxt;
+    use rustc_query_system::query::{deadlock, QueryContext};
+
+    let registry = sync::Registry::new(threads);
+    let mut builder = rayon::ThreadPoolBuilder::new()
         .thread_name(|_| "rustc".to_string())
         .acquire_thread_handler(jobserver::acquire_thread)
         .release_thread_handler(jobserver::release_thread)
         .num_threads(threads)
-        .deadlock_handler(|| unsafe { handle_deadlock() });
-
+        .deadlock_handler(|| {
+            // On deadlock, creates a new thread and forwards information in thread
+            // locals to it. The new thread runs the deadlock handler.
+            let query_map = tls::with(|tcx| {
+                QueryCtxt::new(tcx)
+                    .try_collect_active_jobs()
+                    .expect("active jobs shouldn't be locked in deadlock handler")
+            });
+            let registry = rayon_core::Registry::current();
+            thread::spawn(move || deadlock(query_map, &registry));
+        });
     if let Some(size) = get_stack_size() {
-        config = config.stack_size(size);
+        builder = builder.stack_size(size);
     }
 
-    let with_pool = move |pool: &rayon::ThreadPool| pool.install(f);
-
+    // We create the session globals on the main thread, then create the thread
+    // pool. Upon creation, each worker thread created gets a copy of the
+    // session globals in TLS. This is possible because `SessionGlobals` impls
+    // `Send` in the parallel compiler.
     rustc_span::create_session_globals_then(edition, || {
         rustc_span::with_session_globals(|session_globals| {
-            // The main handler runs for each Rayon worker thread and sets up
-            // the thread local rustc uses. `session_globals` is captured and set
-            // on the new threads.
-            let main_handler = move |thread: rayon::ThreadBuilder| {
-                rustc_span::set_session_globals_then(session_globals, || thread.run())
-            };
-
-            config.build_scoped(main_handler, with_pool).unwrap()
+            builder
+                .build_scoped(
+                    // Initialize each new worker thread when created.
+                    move |thread: rayon::ThreadBuilder| {
+                        // Register the thread for use with the `WorkerLocal` type.
+                        registry.register();
+
+                        rustc_span::set_session_globals_then(session_globals, || thread.run())
+                    },
+                    // Run `f` on the first thread in the thread pool.
+                    move |pool: &rayon::ThreadPool| pool.install(f),
+                )
+                .unwrap()
         })
     })
 }
 
-fn load_backend_from_dylib(path: &Path) -> MakeBackendFn {
+fn load_backend_from_dylib(handler: &EarlyErrorHandler, path: &Path) -> MakeBackendFn {
     let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| {
-        let err = format!("couldn't load codegen backend {:?}: {}", path, err);
-        early_error(ErrorOutputType::default(), &err);
+        let err = format!("couldn't load codegen backend {path:?}: {err}");
+        handler.early_error(err);
     });
 
     let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
         .unwrap_or_else(|e| {
-            let err = format!("couldn't load codegen backend: {}", e);
-            early_error(ErrorOutputType::default(), &err);
+            let err = format!("couldn't load codegen backend: {e}");
+            handler.early_error(err);
         });
 
     // Intentionally leak the dynamic library. We can't ever unload it
@@ -239,6 +250,7 @@ fn load_backend_from_dylib(path: &Path) -> MakeBackendFn {
 ///
 /// A name of `None` indicates that the default backend should be used.
 pub fn get_codegen_backend(
+    handler: &EarlyErrorHandler,
     maybe_sysroot: &Option<PathBuf>,
     backend_name: Option<&str>,
 ) -> Box<dyn CodegenBackend> {
@@ -248,10 +260,12 @@ pub fn get_codegen_backend(
         let default_codegen_backend = option_env!("CFG_DEFAULT_CODEGEN_BACKEND").unwrap_or("llvm");
 
         match backend_name.unwrap_or(default_codegen_backend) {
-            filename if filename.contains('.') => load_backend_from_dylib(filename.as_ref()),
+            filename if filename.contains('.') => {
+                load_backend_from_dylib(handler, filename.as_ref())
+            }
             #[cfg(feature = "llvm")]
             "llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new,
-            backend_name => get_codegen_sysroot(maybe_sysroot, backend_name),
+            backend_name => get_codegen_sysroot(handler, maybe_sysroot, backend_name),
         }
     });
 
@@ -269,7 +283,7 @@ pub fn rustc_path<'a>() -> Option<&'a Path> {
 
     const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR");
 
-    RUSTC_PATH.get_or_init(|| get_rustc_path_inner(BIN_PATH)).as_ref().map(|v| &**v)
+    RUSTC_PATH.get_or_init(|| get_rustc_path_inner(BIN_PATH)).as_deref()
 }
 
 fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
@@ -283,101 +297,11 @@ fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
     })
 }
 
-fn sysroot_candidates() -> Vec<PathBuf> {
-    let target = session::config::host_triple();
-    let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()];
-    let path = current_dll_path().and_then(|s| s.canonicalize().ok());
-    if let Some(dll) = path {
-        // use `parent` twice to chop off the file name and then also the
-        // directory containing the dll which should be either `lib` or `bin`.
-        if let Some(path) = dll.parent().and_then(|p| p.parent()) {
-            // The original `path` pointed at the `rustc_driver` crate's dll.
-            // Now that dll should only be in one of two locations. The first is
-            // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The
-            // other is the target's libdir, for example
-            // `$sysroot/lib/rustlib/$target/lib/*.dll`.
-            //
-            // We don't know which, so let's assume that if our `path` above
-            // ends in `$target` we *could* be in the target libdir, and always
-            // assume that we may be in the main libdir.
-            sysroot_candidates.push(path.to_owned());
-
-            if path.ends_with(target) {
-                sysroot_candidates.extend(
-                    path.parent() // chop off `$target`
-                        .and_then(|p| p.parent()) // chop off `rustlib`
-                        .and_then(|p| p.parent()) // chop off `lib`
-                        .map(|s| s.to_owned()),
-                );
-            }
-        }
-    }
-
-    return sysroot_candidates;
-
-    #[cfg(unix)]
-    fn current_dll_path() -> Option<PathBuf> {
-        use std::ffi::{CStr, OsStr};
-        use std::os::unix::prelude::*;
-
-        unsafe {
-            let addr = current_dll_path as usize as *mut _;
-            let mut info = mem::zeroed();
-            if libc::dladdr(addr, &mut info) == 0 {
-                info!("dladdr failed");
-                return None;
-            }
-            if info.dli_fname.is_null() {
-                info!("dladdr returned null pointer");
-                return None;
-            }
-            let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
-            let os = OsStr::from_bytes(bytes);
-            Some(PathBuf::from(os))
-        }
-    }
-
-    #[cfg(windows)]
-    fn current_dll_path() -> Option<PathBuf> {
-        use std::ffi::OsString;
-        use std::io;
-        use std::os::windows::prelude::*;
-        use std::ptr;
-
-        use winapi::um::libloaderapi::{
-            GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
-        };
-
-        unsafe {
-            let mut module = ptr::null_mut();
-            let r = GetModuleHandleExW(
-                GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
-                current_dll_path as usize as *mut _,
-                &mut module,
-            );
-            if r == 0 {
-                info!("GetModuleHandleExW failed: {}", io::Error::last_os_error());
-                return None;
-            }
-            let mut space = Vec::with_capacity(1024);
-            let r = GetModuleFileNameW(module, space.as_mut_ptr(), space.capacity() as u32);
-            if r == 0 {
-                info!("GetModuleFileNameW failed: {}", io::Error::last_os_error());
-                return None;
-            }
-            let r = r as usize;
-            if r >= space.capacity() {
-                info!("our buffer was too small? {}", io::Error::last_os_error());
-                return None;
-            }
-            space.set_len(r);
-            let os = OsString::from_wide(&space);
-            Some(PathBuf::from(os))
-        }
-    }
-}
-
-fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> MakeBackendFn {
+fn get_codegen_sysroot(
+    handler: &EarlyErrorHandler,
+    maybe_sysroot: &Option<PathBuf>,
+    backend_name: &str,
+) -> MakeBackendFn {
     // For now we only allow this function to be called once as it'll dlopen a
     // few things, which seems to work best if we only do that once. In
     // general this assertion never trips due to the once guard in `get_codegen_backend`,
@@ -410,10 +334,9 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M
             .join("\n* ");
         let err = format!(
             "failed to find a `codegen-backends` folder \
-                           in the sysroot candidates:\n* {}",
-            candidates
+                           in the sysroot candidates:\n* {candidates}"
         );
-        early_error(ErrorOutputType::default(), &err);
+        handler.early_error(err);
     });
     info!("probing {} for a codegen backend", sysroot.display());
 
@@ -424,14 +347,14 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M
             sysroot.display(),
             e
         );
-        early_error(ErrorOutputType::default(), &err);
+        handler.early_error(err);
     });
 
     let mut file: Option<PathBuf> = None;
 
     let expected_names = &[
-        format!("rustc_codegen_{}-{}", backend_name, release_str().expect("CFG_RELEASE")),
-        format!("rustc_codegen_{}", backend_name),
+        format!("rustc_codegen_{}-{}", backend_name, env!("CFG_RELEASE")),
+        format!("rustc_codegen_{backend_name}"),
     ];
     for entry in d.filter_map(|e| e.ok()) {
         let path = entry.path();
@@ -452,16 +375,16 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M
                 prev.display(),
                 path.display()
             );
-            early_error(ErrorOutputType::default(), &err);
+            handler.early_error(err);
         }
         file = Some(path.clone());
     }
 
     match file {
-        Some(ref s) => load_backend_from_dylib(s),
+        Some(ref s) => load_backend_from_dylib(handler, s),
         None => {
-            let err = format!("unsupported builtin codegen backend `{}`", backend_name);
-            early_error(ErrorOutputType::default(), &err);
+            let err = format!("unsupported builtin codegen backend `{backend_name}`");
+            handler.early_error(err);
         }
     }
 }
@@ -495,7 +418,7 @@ pub(crate) fn check_attr_crate_type(
                             BuiltinLintDiagnostics::UnknownCrateTypes(
                                 span,
                                 "did you mean".to_string(),
-                                format!("\"{}\"", candidate),
+                                format!("\"{candidate}\""),
                             ),
                         );
                     } else {
@@ -564,6 +487,8 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
     // Only check command line flags if present. If no types are specified by
     // command line, then reuse the empty `base` Vec to hold the types that
     // will be found in crate attributes.
+    // JUSTIFICATION: before wrapper fn is available
+    #[allow(rustc::bad_opt_access)]
     let mut base = session.opts.crate_types.clone();
     if base.is_empty() {
         base.extend(attr_types);
@@ -576,49 +501,71 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
     }
 
     base.retain(|crate_type| {
-        let res = !output::invalid_output_for_target(session, *crate_type);
-
-        if !res {
-            session.warn(&format!(
-                "dropping unsupported crate type `{}` for target `{}`",
-                *crate_type, session.opts.target_triple
-            ));
+        if output::invalid_output_for_target(session, *crate_type) {
+            session.emit_warning(errors::UnsupportedCrateTypeForTarget {
+                crate_type: *crate_type,
+                target_triple: &session.opts.target_triple,
+            });
+            false
+        } else {
+            true
         }
-
-        res
     });
 
     base
 }
 
-pub fn build_output_filenames(
-    input: &Input,
-    odir: &Option<PathBuf>,
-    ofile: &Option<PathBuf>,
-    temps_dir: &Option<PathBuf>,
-    attrs: &[ast::Attribute],
-    sess: &Session,
-) -> OutputFilenames {
-    match *ofile {
+fn multiple_output_types_to_stdout(
+    output_types: &OutputTypes,
+    single_output_file_is_stdout: bool,
+) -> bool {
+    use std::io::IsTerminal;
+    if std::io::stdout().is_terminal() {
+        // If stdout is a tty, check if multiple text output types are
+        // specified by `--emit foo=- --emit bar=-` or `-o - --emit foo,bar`
+        let named_text_types = output_types
+            .iter()
+            .filter(|(f, o)| f.is_text_output() && *o == &Some(OutFileName::Stdout))
+            .count();
+        let unnamed_text_types =
+            output_types.iter().filter(|(f, o)| f.is_text_output() && o.is_none()).count();
+        named_text_types > 1 || unnamed_text_types > 1 && single_output_file_is_stdout
+    } else {
+        // Otherwise, all the output types should be checked
+        let named_types =
+            output_types.values().filter(|o| *o == &Some(OutFileName::Stdout)).count();
+        let unnamed_types = output_types.values().filter(|o| o.is_none()).count();
+        named_types > 1 || unnamed_types > 1 && single_output_file_is_stdout
+    }
+}
+
+pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames {
+    if multiple_output_types_to_stdout(
+        &sess.opts.output_types,
+        sess.io.output_file == Some(OutFileName::Stdout),
+    ) {
+        sess.emit_fatal(errors::MultipleOutputTypesToStdout);
+    }
+    match sess.io.output_file {
         None => {
             // "-" as input file will cause the parser to read from stdin so we
             // have to make up a name
             // We want to toss everything after the final '.'
-            let dirpath = (*odir).as_ref().cloned().unwrap_or_default();
+            let dirpath = sess.io.output_dir.clone().unwrap_or_default();
 
             // If a crate name is present, we use it as the link name
             let stem = sess
                 .opts
                 .crate_name
                 .clone()
-                .or_else(|| rustc_attr::find_crate_name(sess, attrs).map(|n| n.to_string()))
-                .unwrap_or_else(|| input.filestem().to_owned());
+                .or_else(|| rustc_attr::find_crate_name(attrs).map(|n| n.to_string()))
+                .unwrap_or_else(|| sess.io.input.filestem().to_owned());
 
             OutputFilenames::new(
                 dirpath,
                 stem,
                 None,
-                temps_dir.clone(),
+                sess.io.temps_dir.clone(),
                 sess.opts.cg.extra_filename.clone(),
                 sess.opts.output_types.clone(),
             )
@@ -628,26 +575,23 @@ pub fn build_output_filenames(
             let unnamed_output_types =
                 sess.opts.output_types.values().filter(|a| a.is_none()).count();
             let ofile = if unnamed_output_types > 1 {
-                sess.warn(
-                    "due to multiple output types requested, the explicitly specified \
-                     output file name will be adapted for each output type",
-                );
+                sess.emit_warning(errors::MultipleOutputTypesAdaption);
                 None
             } else {
                 if !sess.opts.cg.extra_filename.is_empty() {
-                    sess.warn("ignoring -C extra-filename flag due to -o flag");
+                    sess.emit_warning(errors::IgnoringExtraFilename);
                 }
                 Some(out_file.clone())
             };
-            if *odir != None {
-                sess.warn("ignoring --out-dir flag due to -o flag");
+            if sess.io.output_dir != None {
+                sess.emit_warning(errors::IgnoringOutDir);
             }
 
             OutputFilenames::new(
                 out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(),
-                out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(),
+                out_file.filestem().unwrap_or_default().to_str().unwrap().to_string(),
                 ofile,
-                temps_dir.clone(),
+                sess.io.temps_dir.clone(),
                 sess.opts.cg.extra_filename.clone(),
                 sess.opts.output_types.clone(),
             )
@@ -655,40 +599,12 @@ pub fn build_output_filenames(
     }
 }
 
-#[cfg(not(target_os = "linux"))]
-pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
-    std::fs::rename(src, dst)
-}
-
-/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems
-/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully"
-/// write back the source file before committing the rename in case a developer forgot some of
-/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates.
-///
-/// To avoid triggering this heuristic we delete the destination first, if it exists.
-/// The cost of an extra syscall is much lower than getting descheduled for the sync IO.
-#[cfg(target_os = "linux")]
-pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
-    let _ = std::fs::remove_file(dst);
-    std::fs::rename(src, dst)
-}
-
-/// Returns a version string such as "1.46.0 (04488afe3 2020-08-24)"
-pub fn version_str() -> Option<&'static str> {
+/// Returns a version string such as "1.46.0 (04488afe3 2020-08-24)" when invoked by an in-tree tool.
+pub macro version_str() {
     option_env!("CFG_VERSION")
 }
 
-/// Returns a version string such as "0.12.0-dev".
-pub fn release_str() -> Option<&'static str> {
-    option_env!("CFG_RELEASE")
-}
-
-/// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built.
-pub fn commit_hash_str() -> Option<&'static str> {
-    option_env!("CFG_VER_HASH")
-}
-
-/// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string.
-pub fn commit_date_str() -> Option<&'static str> {
-    option_env!("CFG_VER_DATE")
+/// Returns the version string for `rustc` itself (which may be different from a tool version).
+pub fn rustc_version_str() -> Option<&'static str> {
+    version_str!()
 }